Registriert seit: 19. Jan 2009
Ort: Kirchlinteln (LK Verden)
1.045 Beiträge
Delphi 2009 Professional
|
AW: Kann man die Bits von Reellen Typen auslesen.
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
|
|
Zitat
|