AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Programmieren allgemein Kann man die Bits von Reellen Typen auslesen.
Thema durchsuchen
Ansicht
Themen-Optionen

Kann man die Bits von Reellen Typen auslesen.

Ein Thema von Alallart · begonnen am 30. Mär 2024 · letzter Beitrag vom 31. Mär 2024
Antwort Antwort
Alallart

Registriert seit: 8. Dez 2015
155 Beiträge
 
#1

Kann man die Bits von Reellen Typen auslesen.

  Alt 30. Mär 2024, 21:04
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?
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#2

AW: Kann man die Bits von Reellen Typen auslesen.

  Alt 30. Mär 2024, 21:23
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, set of 0..31 bzw. set of 0..63 .

Oder ganz einfach, die Funktionen der Helper-Records ala Delphi-Referenz durchsuchenTSingleRec nutzen,
oder noch besser die neueren Record-Helper ala Delphi-Referenz durchsuchenTSingleHelper.
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;
https://en.wikipedia.org/wiki/IEEE_754
https://de.wikipedia.org/wiki/IEEE_754
$2B or not $2B

Geändert von himitsu (30. Mär 2024 um 21:56 Uhr)
  Mit Zitat antworten Zitat
Redeemer

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

AW: Kann man die Bits von Reellen Typen auslesen.

  Alt 30. Mär 2024, 22: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
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#4

AW: Kann man die Bits von Reellen Typen auslesen.

  Alt 30. Mär 2024, 22:31
ähhmmmmmmmmäääääääääää .... seit immer?
Die Größe der Typen muß aber übereinstimmen. (mit Ausnahme des "Befehls" Delphi-Referenz durchsuchenOrd, mit bissl Compilermagic)

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.
$2B or not $2B

Geändert von himitsu (30. Mär 2024 um 23:11 Uhr)
  Mit Zitat antworten Zitat
mytbo

Registriert seit: 8. Jan 2007
472 Beiträge
 
#5

AW: Kann man die Bits von Reellen Typen auslesen.

  Alt 31. Mär 2024, 01:32
... der die Werte einer Variable als Bitfolge darstellt. ... Nur klappt das irgendwie nicht. Ich bekomme keine Bits z. B. eines Single ausgelesen. Geht das überhaupt?
Delphi-Quellcode:
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
Bis bald...
Thomas

Geändert von mytbo (31. Mär 2024 um 16:26 Uhr) Grund: Ausgabe hinzugefügt
  Mit Zitat antworten Zitat
hanvas

Registriert seit: 28. Okt 2010
168 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: Kann man die Bits von Reellen Typen auslesen.

  Alt 31. Mär 2024, 11:09
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:

type 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;
hth Ha Joe
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:15 Uhr.
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