Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Float in zwei Integer zerlegen (https://www.delphipraxis.net/185560-float-zwei-integer-zerlegen.html)

Shark99 19. Jun 2015 19:46

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.

Dalai 19. Jun 2015 19:52

AW: Float in zwei Integer zerlegen
 
Zitat:

Zitat von Shark99 (Beitrag 1305802)
Es geht darum dass ich die Zeitangabe (TDateTime) in einem ListView speichern muss und wollte dazu zwei ungenutzte Integer properties verwenden.

Warum dann dieser Umweg? Speichere doch direkt den TDateTime/Double im ListView. Oder hast du mit den Integers sonst noch etwas vor?

MfG Dalai

Shark99 19. Jun 2015 19:54

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.

mkinzler 19. Jun 2015 19:56

AW: Float in zwei Integer zerlegen
 
Dann speichere es in TListItem.Data

Shark99 19. Jun 2015 19:58

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).

Dalai 19. Jun 2015 20:08

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:
var li: TListItem;
    d: TDateTime;
begin
  li:= ListView1.Items.Item[0];
  d:= Now;
  li.Data:= Pointer(d);
end;
Und auslesen geht dann umgekehrt:
Delphi-Quellcode:
var li: TListItem;
    d: TDateTime;
begin
  li:= ListView1.Items.Item[0];
  d:= TDateTime(li.Data);
end;
Man muss nur aufpassen, dass man wirklich exakt den Datentyp ausliest, den man reinsteckt, sonst kann es böse knallen.

MfG Dalai

Popov 19. Jun 2015 20:08

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));

Shark99 19. Jun 2015 20:12

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!

Dalai 19. Jun 2015 20:20

AW: Float in zwei Integer zerlegen
 
Zitat:

Zitat von Shark99 (Beitrag 1305811)
Hab aber wie gesagt Bedenken.

Ich glaube nicht, dass das ein Problem ist, denn es wird da nur ein Pointer auf etwas - hier TDateTime - gespeichert. Oder wie denkst du, funktioniert das bei Objekten, die ewig lange Strings enthalten können, die weit größer als 4 Byte sind?

MfG Dalai

Dejan Vu 19. Jun 2015 20:24

AW: Float in zwei Integer zerlegen
 
Zitat:

Zitat von Shark99 (Beitrag 1305808)
...Es kostet Performance die hier sehr wichtig ist.

Wie kann denn das sein? TListView und Performance? Entschuldige, aber bist Du Dir da ganz sicher? Wir reden hier von einem Windows Control. Da passt das mit dieser 'Performance' einfach nicht.

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...

Shark99 19. Jun 2015 20:34

AW: Float in zwei Integer zerlegen
 
Zitat:

Zitat von Dejan Vu (Beitrag 1305815)
Zitat:

Zitat von Shark99 (Beitrag 1305808)
...Es kostet Performance die hier sehr wichtig ist.

Wie kann denn das sein? TListView und Performance? Entschuldige, aber bist Du Dir da ganz sicher? Wir reden hier von einem Windows Control. Da passt das mit dieser 'Performance' einfach nicht.

Was willst Du eigentlich machen?

Ich habe ein dynamisches Array mit etwa 100k records. In einem Listview werden max 1000 davon angezeigt.

Zitat:

@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...
Meine Befürchtung ist eben dass ein TDateTime, also ein double 8 Bytes belegt, wenn ich das in einen 4 Byte Pointer reinquetsche können Daten verloren gehen.

Shark99 19. Jun 2015 20:35

AW: Float in zwei Integer zerlegen
 
Zitat:

Zitat von Dalai (Beitrag 1305813)
Zitat:

Zitat von Shark99 (Beitrag 1305811)
Hab aber wie gesagt Bedenken.

Ich glaube nicht, dass das ein Problem ist, denn es wird da nur ein Pointer auf etwas - hier TDateTime - gespeichert. Oder wie denkst du, funktioniert das bei Objekten, die ewig lange Strings enthalten können, die weit größer als 4 Byte sind?

MfG Dalai

Ein String ist ein 4 Byte Pointer und passt somit in einen "normalen" Pointer. Ein Double hat 8 Bytes.

Luckie 19. Jun 2015 20:43

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.

Shark99 19. Jun 2015 20:45

AW: Float in zwei Integer zerlegen
 
Wie ich schrieb, ein dynamisches Array von Records, im Record selbst ist TDateTime (index) und ein string.

Luckie 19. Jun 2015 20:51

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?

Shark99 19. Jun 2015 20:57

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.

Dalai 19. Jun 2015 21:02

AW: Float in zwei Integer zerlegen
 
Die Frage ist: Was, also welche Operation, war langsamer? Lesen? Objekte erzeugen? Verschieben? Sortieren? Löschen?

MfG Dalai

Shark99 19. Jun 2015 21:05

AW: Float in zwei Integer zerlegen
 
Im Prinzip war alles langsamer. Verwende TDynArray, welche in Gegensatz zu TList sehr optimiert ist.

BUG 19. Jun 2015 21:06

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.

Shark99 19. Jun 2015 21:08

AW: Float in zwei Integer zerlegen
 
Weil das Array aus Records und nicht aus Objekten besteht.

BUG 19. Jun 2015 21:12

AW: Float in zwei Integer zerlegen
 
Zitat:

Zitat von Shark99 (Beitrag 1305826)
Weil das Array aus Records und nicht aus Objekten besteht.

Wo ist das Problem? Im Prinzip kannst du auf jede Stelle im Speicher zeigen, egal was da für ein Datentyp liegt. Records sind da keine Ausnahme.

Shark99 19. Jun 2015 21:14

AW: Float in zwei Integer zerlegen
 
Ich dachte ich kann mich bei einem dymanischen Array nicht darauf verlassen wegen Inserts, Deletes etc.

Popov 19. Jun 2015 21:19

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:
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;
Um das Beispiel umzusetzen reicht ein ListView auf dem Formular.

BUG 19. Jun 2015 21:25

AW: Float in zwei Integer zerlegen
 
Zitat:

Zitat von Shark99 (Beitrag 1305828)
Ich dachte ich kann mich bei einem dymanischen Array nicht darauf verlassen wegen Inserts, Deletes etc.

Stimmt natürlich. Das könnte man nachprüfen, aber ich habe keine Lust mich in dieses TDynArray-Monster einzulesen.

Dann nimm halt das nächstbeste: Der Index in das Array (auf Pointer gecastet).

Shark99 19. Jun 2015 21:30

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.

Popov 19. Jun 2015 21:54

AW: Float in zwei Integer zerlegen
 
Zitat:

Zitat von Shark99 (Beitrag 1305832)
Hatte zuerst TList, aber nach einem Tipp auf TDynArray umgestellt und jede Operation (Insert, Delete, Move, Copy von Teilbereichen) ist 30-300% schneller.

Intern koch die Mama auch nur mit Wasser.

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?

Shark99 19. Jun 2015 22:08

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.

Stevie 19. Jun 2015 22:19

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:

Shark99 19. Jun 2015 22:22

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.

Shark99 19. Jun 2015 22:32

AW: Float in zwei Integer zerlegen
 
Wer Interesse an TDynArray hat, kann ich wärmstens empfehlen:

http://synopse.info/forum/viewtopic.php?id=254

http://synopse.info/files/mORMotNightlyBuild.zip

Popov 19. Jun 2015 22:51

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.

Stevie 19. Jun 2015 23:46

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 hier und hier)

Shark99 20. Jun 2015 07:57

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.

mkinzler 20. Jun 2015 08:02

AW: Float in zwei Integer zerlegen
 
Zitat:

Zitat von Shark99 (Beitrag 1305853)
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.

Und 58898345 / 24 / 60 / 60 / 1000 ergibt wieder 0,68169380787037

Shark99 20. Jun 2015 08:04

AW: Float in zwei Integer zerlegen
 
Eben, es gibt Rundungsfehler und ich finde den Record nicht wieder.

Dejan Vu 20. Jun 2015 08:14

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.

Shark99 20. Jun 2015 08:21

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.

Dejan Vu 20. Jun 2015 08:25

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.

Shark99 20. Jun 2015 08:31

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));

Dejan Vu 20. Jun 2015 08:35

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:
t := GetTickCount;
for i:=1 to 1000 do foo:= THilfsObjekt.Create;
Writeln (GetTickCount - t);
Ausgabe: 0 oder 18 (ms). Garantiert.


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:51 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz