![]() |
Delphi-Version: 2009
Float in zwei Integer zerlegen
Ich möchte eine Gleitzahl in zwei Integer zerlegen.
Vorkomma und Nachkomma sollen jeweils zwei Integer bilden. Später will ich das ganze zusammensetzen. Es sollte möglichst performant sein, also kein Umweg über Strings. Vorkomma ist ganz einfach.
Delphi-Quellcode:
vk_int := Trunc(float1);
Float Nachkommanteil auch nocht recht einfach.
Delphi-Quellcode:
nk_float := Frac(float1);
Wie kann ich aber nun das ganze in eine Ganzzahl wandeln, also z.B. wenn nk_float 0.345 ist, wie mache ich eine 345 daraus und bei 0.14 eine 14? Wenn ich die beiden Integer dann habe, wie füge ich die Nachkommastelle wieder ein um ein Float zu bilden (um das ganze rückgängig zu machen). -- Es geht darum dass ich die Zeitangabe (TDateTime) in einem ListView speichern muss und wollte dazu zwei ungenutzte Integer properties verwenden. |
AW: Float in zwei Integer zerlegen
Zitat:
MfG Dalai |
AW: Float in zwei Integer zerlegen
Ich kann die Zeit nicht in dem Listview (Items) direkt speichern, weil der User sie nicht sehen sollte.
Ich habe Daten in einem dynamischen Array (muss sehr performant sein, TList kommt nicht in Frage). Ich zeige dann Daten daraus in einem Listview. Der Array ist nach Zeit indiziert (ohne Duplikate) und ich muss ein TDateTime in das Listview irgendwie reinquetschen um schnell ein Listview Item wieder im Array zu finden. |
AW: Float in zwei Integer zerlegen
Dann speichere es in TListItem.Data
|
AW: Float in zwei Integer zerlegen
Das war meine erste Idee, aber dazu muss ich mit Objekten rumhantieren, Speicher allozieren etc. Es kostet Performance die hier sehr wichtig ist. Ich dachte ich zerlege TDateTime in zwei Integer und speichere sie im ImageIndex (Vorkomma) und OverlayIndex (die im Listview nicht verwendet werden).
|
AW: Float in zwei Integer zerlegen
Und? Es hindert dich doch keiner daran, den Wert in TListItem.Data abzulegen.
TListView.Items beinhaltet alle Items und ist vom Typ TListItems. TListItems.Item wiederum bietet Zugriff auf jedes einzelne TListItem. Und TListItem.Data ist ein beliebig verwendbarer Pointer. Das ginge in etwa so:
Delphi-Quellcode:
Und auslesen geht dann umgekehrt:
var li: TListItem;
d: TDateTime; begin li:= ListView1.Items.Item[0]; d:= Now; li.Data:= Pointer(d); end;
Delphi-Quellcode:
Man muss nur aufpassen, dass man wirklich exakt den Datentyp ausliest, den man reinsteckt, sonst kann es böse knallen.
var li: TListItem;
d: TDateTime; begin li:= ListView1.Items.Item[0]; d:= TDateTime(li.Data); end; MfG Dalai |
AW: Float in zwei Integer zerlegen
Ich hab die letzten paar Tage in einer Zeitschleife zu hängen, alternativ permanent Deja-Vus zu erleben. Jeder will plötzlich Datum und Zeit zerlegen.
Ich hab jetzt keine Zeit um zu zeigen wie das mit Objekten klappt, vielleicht später (Edit: Dalai hat es gerade gemacht). Hier die Integer-Variante:
Delphi-Quellcode:
Datum := Trunc(Now);
Zeit := Trunc(Frac(Now) * 24 * 60 * 60 * 1000); //und zurück DatumUndZeit := Datum + (Zeit / 24 / 60 / 60 / 1000); ShowMessage(DateTimeToStr(DatumUndZeit)); |
AW: Float in zwei Integer zerlegen
@Dalai
Ich habe angenommen dass unter 32Bit ein Pointer aus 4 Bytes besteht und der Platz nicht ausreicht um eine Gleitkommazahl darin abzuspeichern. Es wäre Klasse wenn ich TDateTime direkt in .Data reinpressen kann. Hab aber wie gesagt Bedenken. @Popov Danke! |
AW: Float in zwei Integer zerlegen
Zitat:
MfG Dalai |
AW: Float in zwei Integer zerlegen
Zitat:
Was willst Du eigentlich machen? @Dalai: Do speicherst keinen Zeiger auf ein TDateTime, sondern Du kopierst die obersten (untersten, je nach Blickwinkel) 4 Bytes des TDateTime in einen 32-bit Wert. Kleiner Unterschied... |
AW: Float in zwei Integer zerlegen
Zitat:
Zitat:
|
AW: Float in zwei Integer zerlegen
Zitat:
|
AW: Float in zwei Integer zerlegen
Sag mal was leuigt dem eigentlich eien Datenstruktur zu Grunde? Wo kommen die anderen Daten her, die im Listview angezeigt werden? Da muss es doch auch eien Datenstruktur geben.
|
AW: Float in zwei Integer zerlegen
Wie ich schrieb, ein dynamisches Array von Records, im Record selbst ist TDateTime (index) und ein string.
|
AW: Float in zwei Integer zerlegen
Mit Objekten wäre es einfacher.
Von wie vielen Datensätzen reden wir und warum ist die Performance so wichtig? |
AW: Float in zwei Integer zerlegen
Es wäre einfacher, aber hab derzeit 100k Datensätze und es können mehr werden. Habs mit TList und Objekten getestet. War etwa 2-3 mal langsamer.
|
AW: Float in zwei Integer zerlegen
Die Frage ist: Was, also welche Operation, war langsamer? Lesen? Objekte erzeugen? Verschieben? Sortieren? Löschen?
MfG Dalai |
AW: Float in zwei Integer zerlegen
Im Prinzip war alles langsamer. Verwende TDynArray, welche in Gegensatz zu TList sehr optimiert ist.
|
AW: Float in zwei Integer zerlegen
Kann sein das ich dich falsch verstanden habe, aber wieso speicherst du in dem TListView-Zeiger nicht einfach den Zeiger auf die eigentliche Datenstruktur in dem Array? Das könnte zwar Probleme geben, wenn du die Größe des Arrays änderst (Reallokation), aber ansonsten solltest du damit glücklich werden.
|
AW: Float in zwei Integer zerlegen
Weil das Array aus Records und nicht aus Objekten besteht.
|
AW: Float in zwei Integer zerlegen
Zitat:
|
AW: Float in zwei Integer zerlegen
Ich dachte ich kann mich bei einem dymanischen Array nicht darauf verlassen wegen Inserts, Deletes etc.
|
AW: Float in zwei Integer zerlegen
In der letzten Zeit bin ich ein Fan von virtuellen Listen, sowohl bei ListBoxen, wie auch bei ListViews. Verwaltet werden die Daten in einer ObjectList und lediglich angezeigt in ListView. Der Vorteil, dadurch wird das Ganze viel schneller. Der Grund - hier muss ListView nicht die ganzen Daten schleppen, die bleiben in der ObjectList. ListView ist nur noch für die Anzeige der paar Items die gerade sichtbar sind:
Delphi-Quellcode:
Um das Beispiel umzusetzen reicht ein ListView auf dem Formular.
uses
Contnrs; type TTest = class Num: Integer; DateTime: TDateTime; end; var ol: TObjectList; procedure TForm1.FormCreate(Sender: TObject); var d: Integer; Test: TTest; Col: TListColumn; begin //Hier eine ObjectList Liste erstellen ol := TObjectList.Create(True); //Hier die Liste mit Daten füllen for d := Trunc(Now) - 365 to Trunc(Now) do begin Test := TTest.Create; Test.Num := Random(1000); Test.DateTime := d + Now; ol.Add(Test); end; with ListView1 do begin ViewStyle := vsReport; Col := Columns.Add; //Beschriftungen Col.Caption := 'Num'; Col := Columns.Add; Col.Caption := 'Date'; Col.Width := 150; OwnerData := True; Items.Count := ol.Count; end; end; procedure TForm1.FormDestroy(Sender: TObject); begin ol.Free; end; procedure TForm1.ListView1Data(Sender: TObject; Item: TListItem); var Test: TTest; begin Test := TTest(ol[Item.Index]); //hier werden die Daten aus ObjectList an ListView übertragen Item.Caption := IntToStr(Test.Num); Item.SubItems.Add(DateTimeToStr(Test.DateTime)); end; |
AW: Float in zwei Integer zerlegen
Zitat:
Dann nimm halt das nächstbeste: Der Index in das Array (auf Pointer gecastet). |
AW: Float in zwei Integer zerlegen
@Popov
Hatte zuerst TList, aber nach einem Tipp auf TDynArray umgestellt und jede Operation (Insert, Delete, Move, Copy von Teilbereichen) ist 30-300% schneller. @Bug Ich kann eben den Index (also array[x]) nicht speichern, weil dieser sich ständig ändert. Ein Delete oder Insert und der Index zeigt schon auf einen anderen Record. Ich muss also das TDateTime speichern und wenn ich von ListView -> Array gehen will eine Suche machen. |
AW: Float in zwei Integer zerlegen
Zitat:
Aber entweder ich verstehe nicht was du machst oder du verstehst nicht was ich vorhin gepostet habe. Ich hab letztens ein Beispiel hier im Forum erstellt das 13.000 Dateiennamen von der Festplatte, inkl. Größe und Datum und alphabetischen Sortierung, in 15 ms in die Liste einliest und sie in einer ListView darstellt. Du hast 1.000 Werte. Willst du mit mir also über eine 1/2 Millisekunde diskutieren? |
AW: Float in zwei Integer zerlegen
Ich hab 100.000 Werte (wie mehrmals geschrieben) und es werden mehr. Diese werden mit anderen Arrays abgeglichen, es wird nach Duplikaten gesucht etc. Manche Operationen dauern dann 2-3 Sekunden und wenn ich bei der Umstellung von TList auf TDynArray 1 Sekunde gewinne ist es großartig.
|
AW: Float in zwei Integer zerlegen
Ich rieche Optimierung an der falschen Stelle.
Wenn bei 100k Elementen bestimmte Operationen 2-3 Sekunden dauern, dann liegt das ganz sicher daran, dass ein falscher Algorithmus benutzt wird (beim Suchen z.B. vermute ich eine lineare Suche) - außerdem kommen bei Records noch andere mögliche Performance hits hinzu (z.B. durch mangelndes const bei Parameterübergabe haufenweise CopyRecord). Wie sind wir nochmal vom Zerlegen eines Floats in 2 Integer hierher gekommen? :shock: |
AW: Float in zwei Integer zerlegen
Es ist viel Suche nach Duplikaten dabei, wenn neue Daten ankommen, da kann ich nicht viel optimieren, weil ich schon eine Hashtable für die Stringwerte verwende. Die Inserts und Deletes sind sowohl bei TList als auch bei dynamischen Arrays extremst kostspielig. Deshalb dachte ich über eine linked List oder ein Binary Tree nach, aber dann renne ich in andere Nachteile.
|
AW: Float in zwei Integer zerlegen
Wer Interesse an TDynArray hat, kann ich wärmstens empfehlen:
![]() ![]() |
AW: Float in zwei Integer zerlegen
Mal ein kleiner Tipp: lass das mit den Delete und Insert, das ist im Grunde Quatsch, denn du schiebst ständig Datenmassen hin und her. Ich hab zwar einen kleine Gedankenfehler gehabt, denn ich hab TDynArray überlesen und dynamische Array verstanden. Trotzdem, viel ändert sich dadurch nicht.
Statt zu löschen, erstelle eine temporäre Liste in die du die Daten kopierst die du benötigst. Das ist schneller, denn jedes Mal wenn du mit Delete etwas löscht, muss alles was drüber ist nach unten verschoben werden. Das kostet Zeit. Und wenn du mit Insert einfügst, muss erst Platz geschaffen werden. Dein Programm ist die ganze Zeit mit Datenschieben beschäftigt. Anders ist es wenn du stattdessen die Daten die du brauchst in eine neue Liste einfügst. Es wird alles nach oben aufeinander gestapelt. Um das Ganze noch schneller zu machen musst du die Größe der Liste grob einschätzen und den nötigen Speicher mit Capacity reservieren. Ich kenne zwar nicht TDynArray, aber vermutlich arbeitet sie ähnlich wie andere Listen und hat die Eigenschaft Capacity. Die am Anfang groß genug ansetzen, dann muss nicht ständig neuer Platz reserviert werden. Falls man zuviel reserviert hat, kann man es später wieder reduzieren. |
AW: Float in zwei Integer zerlegen
TDynArray is nicht viel mehr als nen Wrapper über nen dynamisches Array mit einigen Lowlevel Optimizations - kein großes Zauberwerk.
Wenn man dort drin sucht und die Daten nicht sortiert vorliegen hat, dann wird das auch keinen Geschwindigkeitsrekord bringen. Und ne Hashtable bringt auch nur dann was, wenn die Hashberechnung nicht zu teuer wird (guckstu ![]() ![]() |
AW: Float in zwei Integer zerlegen
Mal zum Ursprungsthema.
Irgendwas ist faul. Aus einem TDateTime von 42173,681694 wird durch Trunc(Frac(DateTime) * 24 * 60 * 60 * 1000) ein Integer von 58898345. Werde es wohl doch in einen String speichern müssen damit ich es wieder finde. |
AW: Float in zwei Integer zerlegen
Zitat:
|
AW: Float in zwei Integer zerlegen
Eben, es gibt Rundungsfehler und ich finde den Record nicht wieder.
|
AW: Float in zwei Integer zerlegen
ich habe es immer noch nicht verstanden: Du hast ein Array mit irgendwelchen TDateTime-Werten und willst 1000 davon darstellen. Sind das immer die gleichen, d.h. filterst Du die irgendwie und willst sie dann darstellen? Ist eigentlich auch egal, denn: 1000x irgendein Objekt erzeugen, dauert geschätzte 0.01 Sekunden. Oder weniger.
Anders ausgedrückt: Du kannst beruhigt 1000 Objekte erzeugen, die deinen Record beinhalten. Allerdings dürfte der Rest deiner Programmierung das Performanceproblem sein. Und falls Du mal 100.000 Werte (oder von mir aus 100 Mio Werte) in einer Listbox darstellen willst, verwende den virtual Mode, wie ihn Popov skizziert hat. |
AW: Float in zwei Integer zerlegen
Es geht darum dass ich einen Eintrag aus dem Listview wieder in meinem TDynArray finden will.
Als ich noch TList verwendete war es einfach. Hab in TListItem.Data den Pointer zu einem TList Objekt gespeichert und das wars. Mit dem dynamischen Array kann ich keinen Pointer nehmen. Also muss ich den Array durchsuchen. Ein Record besteht aus einem TDateTime (als Index) und einem string. Der User kann sagen zeig mir Daten eines Tages aus dem Array in einem Listview an. ListItem.Caption ist also schon mal der string aus dem Record. Aber nun muss ich TDateTime irgendwie in dem Listview abspeichern so dass ich es wieder finde. Werde wohl ein Objekt mit dem TDateTime erzeugen müssen und ein .Data anhängen. Dachte ich kriege es irgendwie anders hin. |
AW: Float in zwei Integer zerlegen
Ahhh...
Verwende eine Dictionary zum schnellen Finden. Dein dynamisches Array ist einfach die falsche Struktur. Versuchs mal mit einem Baum, einer Skiplist oder eine Dictionary. Damit geht das Einfügen und Suchen sehr schnell (Dictionary ist am schnellsten). Nachteil der Dictionary: Die Daten liegen nicht sortiert vor. Aber wenn man das nicht braucht, würde ich die Dictionary nehmen. Oder den. Oder das. Je nach persönlicher Präferenz. |
AW: Float in zwei Integer zerlegen
So einfach ist das nicht; ich nutze schon viele Sachen des TDynArrays, die ich sonst ersetzen müsste durch andere Komponenten, z.b. SaveToStream() um ein UTF8 JSON zu erzeugen. Mit der Performance bin auch schon sehr zufrieden, ist alles gut doppelt so schnell geworden wie die ursprüngliche TList Geschichte.
Hab eben bemerkt dass ich TDateTime -> string und zurück Convertions auch unbedingt vermeiden sollte. DateTime <> StrToFloat(FloatToStr(DateTime)); |
AW: Float in zwei Integer zerlegen
Da sieht man mal wieder, das man die konkrete Implementierung kapseln/verbergen sollte. Nun hast Du den Salat. Aber im Ernst: Nicht die paar 1000 Objekte sind das Problem, sondern deine Liste.
Also: Wenn Du die 1000 Elemente in der Liste erzeugst, instantiiere dir einfach 1000 Hilfsobjekte, die das TDateTime enthalten. Ich gehe jede Wette ein, das das schnell genug geht:
Delphi-Quellcode:
Ausgabe: 0 oder 18 (ms). Garantiert.
t := GetTickCount;
for i:=1 to 1000 do foo:= THilfsObjekt.Create; Writeln (GetTickCount - t); |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:01 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz