![]() |
Kann man die Bits von Reellen Typen auslesen.
Ich bin gerade an einem kleinem Projekt, der die Werte einer Variable als Bitfolge darstellt. Die Schüler eines Informatik-Kurses wollen überprüfen ob die Bits, die sie beim Binärrechnen rechen, tatsächlich so auch im Computer gespeichert werden.
Ich habe das für die meisten Ganzzahl Typen umgesetzt, und kam auf die Idee es auch mit Reellen Typen zu versuchen. Nur klappt das irgendwie nicht. Ich bekomme keine Bits z. B. eines Single ausgelesen. Geht das überhaupt? |
AW: Kann man die Bits von Reellen Typen auslesen.
Einfach Single wie gewünscht nach Integer/Cardinal/LongInt/LongUInt/LongWord casten, bzw. Double nach Int64/UInt64/TLageInteger und dann beliebige Binäroperationen, wie z.B. SHR bzw. AND.
Oder in ein BitField casten, wie TIntegerSet,
Delphi-Quellcode:
bzw.
set of 0..31
Delphi-Quellcode:
.
set of 0..63
Oder ganz einfach, die Funktionen der Helper-Records ala ![]() oder noch besser die neueren Record-Helper ala ![]()
Delphi-Quellcode:
var
F: Single; L: LongUInt; i := LongInt(F); S := Odd(i shr 31); S := i shr 31 and $1 <> 0; S := i and $80000000 <> 0; S := i and (1 shl 31) <> 0; ...
Delphi-Quellcode:
var F: Single;
F := 123.456; E := TSingleRec(F).Exp; F := TSingleRec(F).Frac; S := TSingleRec(F).Sign; E := TSingleRec(F).Exponent; M := TSingleRec(F).Mantissa; S := TSingleRec(F).Sign; B1 := TSingleRec(F).Bytes[0]; B2 := TSingleRec(F).Bytes[1]; ... S := TSingleRec(F).Bytes[3] and $80 <> 0;
Delphi-Quellcode:
uses SysUtils;
var F: Single; F := 123.456; E := F.Exp; F := F.Frac; S := F.Sign; E := F.Exponent; M := F.Mantissa; S := F.Sign; B1 := F.Bytes[0]; B2 := F.Bytes[1]; ... S := F.Bytes[3] and $80 <> 0; ![]() ![]() |
AW: Kann man die Bits von Reellen Typen auslesen.
Seit wann kann man eigentlich so etwas casten? Das ging früher nicht und man musste über Pointer gehen:
Delphi-Quellcode:
Das ist nach meiner Erfahrung die korrekte Möglichkeit, selbst Gleitkommazahlen verlustfrei zur Übertragung an einen Datenbankserver zu maskieren.
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; |
AW: Kann man die Bits von Reellen Typen auslesen.
ähhmmmmmmmmäääääääääää .... seit immer?
Die Größe der Typen muß aber übereinstimmen. (mit Ausnahme des "Befehls" ![]() Klar, Pointern geht auch, aber beim direkten Cast zu Typ oder Record, prüft der Compiler auch die Größe, damit man nicht ausversehn auf nichtexistierenden Speicher zugreift. |
AW: Kann man die Bits von Reellen Typen auslesen.
Zitat:
Delphi-Quellcode:
Bis bald...
function BinToStr(pmValue: Pointer; pmTypeInfo: Pointer): String;
const CHAR_MAP: array[Boolean] of Byte = (48, 49); // 0, 1 var i: Integer; isBit: Boolean; bitCount: Integer; rttiType: TRttiType; begin Result := ''; if pmValue = Nil then Exit; //=> if pmTypeInfo = Nil then Exit; //=> rttiType := TRttiContext.Create.GetType(pmTypeInfo); if rttiType <> Nil then begin bitCount := rttiType.TypeSize * 8; SetLength(Result, bitCount); for i := 0 to bitCount - 1 do begin isBit := (PByteArray(pmValue)[i shr 3] and (1 shl (i and 7)) <> 0); Result[bitCount - i] := Chr(CHAR_MAP[isBit]); end; end; end; begin var i: Integer := 5; ShowMessage(BinToStr(@i, TypeInfo(Integer))); // Ausgabe: 00000000000000000000000000000101 var d: Single := 50.0; ShowMessage(BinToStr(@d, TypeInfo(Single))); // Ausgabe: 01000010010010000000000000000000 Thomas |
AW: Kann man die Bits von Reellen Typen auslesen.
Das geht entweder mit einem Cast oder z. B. auch mit varianten Records. Das funktioniert in jeder Delphi Version bzw. jedem Pascal Compiler (FPC) was ja bei deinem Publikum möglicherweise eine Rolle spielt.
Code:
hth Ha Joetype TVariantSingleInteger = record Case Byte of 0 : (s : Single); 1 : (i : Integer); end; ... function Single2Integer(const s : Single) : Integer; var r : TVariantSingleInteger; begin r.s := s; result := r.i; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:25 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-2025 by Thomas Breitkreuz