Einzelnen Beitrag anzeigen

Redeemer

Registriert seit: 19. Jan 2009
Ort: Kirchlinteln (LK Verden)
1.018 Beiträge
 
Delphi 2009 Professional
 
#3

AW: Kann man die Bits von Reellen Typen auslesen.

  Alt 30. Mär 2024, 21:11
Seit wann kann man eigentlich so etwas casten? Das ging früher nicht und man musste über Pointer gehen:
Delphi-Quellcode:
function RealEscapeString(const f: Double; const Represents: TDoubleRepresentation): string;
function RealEscapeStringDT(const dt: TDateTime): string;
begin
  // SQL Server versteht Delphis TDateTime nur scheinbar, denn er ist dann 2 Tage zu spät.
  // Result := FormatDateTime('"DATETIMEFROMPARTS("yyyy,mm,dd,hh,nn,ss,zzz")"', dt); // SQL Server 2012+
  Result := 'CAST(' + RealEscapeString(SysUtils.FormatDateTime('yyyy-mm-dd"T"hh":"nn":"ss.zzz', dt)) + ' as DATETIME)'; // Syntax, die für MySQL und Transact JEWEILS Big Endian ist (ohne "T" ist es bei MSSQL Middle Endian; ohne Bindestriche und "T" ist es bei MSSQL Big Endian und bei MySQL ungültig)
  // Das CAST dient für den Fall, dass ein FakeSelect ausgeführt wird. Es verhindert aber die Nutzung bei Aufrufen von Stored Procedures ohne Variablen.
  // CAST ist ANSI-SQL, CONVERT nicht und z.B. in MySQL und MSSQL mit unterschiedlicher Parameterreihenfolge implementiert
end;
var
  Pointer: PInt64;
  Vorzeichen: Boolean;
  Mantisse: Int64;
  Exponent: Word;
  EchterExponent: SmallInt;
  EchteMantisse: Int64;
begin
  if Represents = drTDateTime then
  Result := RealEscapeStringDT(f)
  else
  begin
    // Diese Methode führt eine verlustfreie Umwandlung von Double in eine Zahl in SQL-Quelltext durch.
    // Da das Ergebnis kein Literal ist, funktioniert sie nicht in Aufrufen von Stored Procedures, ohne eine Variable zu verwenden, in die sie vorher geschrieben worden ist.
    // Das Ergebnis funktioniert allerdings sogar in MySQL.
    Pointer := @f;
    Vorzeichen := Pointer^ shr 63 <> 0;
    Exponent := (Pointer^ shr 52) and $7FF;
    Mantisse := Pointer^ and $FFFFFFFFFFFFF;

    // Echten Exponenten bestimmen
    // -1023 ist der Bias nach IEEE 754.
    // Die Mantisse beschreibt die 52 Nachkommastellen einer Binärzahl, die wir allerdings durch einen Exponenten von -52 erst zu Nachkommastellen machen müssen.
    EchterExponent := SmallInt(Exponent) - 1023 - 52;

    // Ist der Exponent nicht der niedrigst- oder höchstmögliche Wert, so ist die Zahl normalisiert.
    // Bei normalisierten Zahlen steht immer eine 1 vorm Komma, weshalb sie nicht in der Mantisse gespeichert wird.
    // Wir müssen sie also hinzufügen.
    // Normalisierte Zahlen gibt es nur im Binärsystem, weil es in anderen Systemen mehr sinnvolle Vorkommazahlen gibt, die hinzugefügt werden müssten.
    if (Exponent <> 0) and (Exponent <> $7FF) then
    EchteMantisse := Mantisse or $10000000000000
    else
    EchteMantisse := Mantisse;

    // Vorzeichen hinzufügen
    if Vorzeichen then
    EchteMantisse := -EchteMantisse;

    // Exponent = 0: Nicht normierte Zahlen (also solche, bei denen die erste Stelle (=Vorkommastelle) der Mantisse nicht 1 ist
    // Der echte Exponent ist trotzdem -1074 und nicht -1075.
    if Exponent = 0 then
    Inc(EchterExponent);

    // Ausgabe
    if (Exponent = 0) and (Mantisse = 0) then // nicht echte Mantisse!
    Result := '0// Vorzeichen zu beachten schenken wir uns, der SQL-Server unterstützt nämlich -0 nicht
    else
    if Exponent = $7FF then
    Result := 'NULL// eigentlich NaN, unendlich und -unendlich, aber das wird alles drei nicht unterstützt
    else
    Result := '(' + IntToStr(EchteMantisse) + 'e0 * POWER(2e0, ' + IntToStr(EchterExponent) + '))'; // das ständige e0 führt dazu, dass POWER eine Kommazahl zurückgibt
  end;
end;
Das ist nach meiner Erfahrung die korrekte Möglichkeit, selbst Gleitkommazahlen verlustfrei zur Übertragung an einen Datenbankserver zu maskieren.
Janni
2005 PE, 2009 PA, XE2 PA
  Mit Zitat antworten Zitat