13

Is simple way to convert double value to its bytes representation? I tried using pointers like:

var
  double_v:double;
  address:^double;
....
double_v:=100;
address:=@double_v;

but every my concepts: how to read thise 8 bytes from address, end with "AV".

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
Artik
  • 803
  • 14
  • 36

3 Answers3

16

Use a variant record

Type
  TDoubleAndBytes = Record
    case boolean of
      false : (dabDouble : Double);
      true  : (dabBytes : Array [0..7] Of Byte);
  end;

Assign the double value to dabDouble and read the bytes through dabBytes

var
  myVar : TDoubleAndBytes;
  i : integer;

begin
  myVar.dabDouble := 100;
  for i:=0 to 7 do write(myVar.dabBytes[i]);
alzaimar
  • 4,572
  • 1
  • 16
  • 30
  • Impossibly fast and helpful reply! Thank you very much. – Artik Dec 29 '12 at 11:34
  • To be sure that the variant parts overlap, you should always use a *packed* record for these purposes. In this case it doesn't make a difference (so you were lucky), but in general, this trick only works with packed records. – Rudy Velthuis Dec 29 '12 at 15:41
  • @Rudy: It is not necessary in this case but as a rule of thumb, you're right. – alzaimar Dec 29 '12 at 19:21
  • @Rudy That's poor advice. Each variant in the variant part of the record will start at the same address, even if the containing variant record is aligned. Making a record packed is a disaster for alignment and performance. Doing it here would put a `double` away from an 8 byte boundary. Which will lead to runtime failure for SSE code. For example, x64 floating point. The key, and this is not well understood in my experience, is that `packed` affects both internal layout, and overall alignment. – David Heffernan Dec 29 '12 at 22:53
  • @DavidHeffernan: Can I test this with a core i7 and BDS 2006? A simple test works (`packed record filler : byte; case boolean...`) – alzaimar Dec 30 '12 at 08:09
  • @alzaimer mis alignment penalty is just performance on x86. You need to be running x64 and using certain SSE fp instructions to see runtime errors. – David Heffernan Dec 30 '12 at 08:17
  • Thx, of course, my old BDS2006 creates x86 code. Ok, so the restriction applies to packed records, new x64 compilers and handmade assembler code, right? – alzaimar Dec 30 '12 at 08:54
  • @David: It is not poor advice at all, especialy if there are multiple variant parts, some of which could be composed of various fields. Then it is very important that the record is packed. I already said that in this case, he is lucky. -- This record was only created in order to map two different types to the same memory. In that case, records should be packed. Generally, records should NOT be, I agree. – Rudy Velthuis Dec 30 '12 at 15:42
  • @rudy It's awful advice. Ruin the alignment of the entire record? Alignment really matters for performance sensitive code. – David Heffernan Dec 30 '12 at 15:51
  • @David: this record was only created to map the two types to the same piece of memory. Such records should ideally be packed. In general, if the requirement is that variant parts map properly, you'll have to use packed. If that is not a requirement, you should not use packed but properly align records. I have the feeling you would agree with me if I could give an example in this limited space. – Rudy Velthuis Dec 30 '12 at 16:05
  • @Rudy Well, we disagree. There's an important distinction between layout and alignment. Packed is a crude tool that influences both. I would leave the containing record aligned, and be precise about the layout of its contents. If necessary by placing them inside another container. – David Heffernan Dec 30 '12 at 16:18
  • @David: packed is a requirement if you want to exactly MAP two different types to exactly the same memory, and that was exactly what is being done here. Otherwise I agree that one should use proper alignment. – Rudy Velthuis Dec 30 '12 at 16:30
  • @Rudy I'm afraid that you are wrong. The first fields in the variant part of the record are always at the same address. Whether the containing record is packed or aligned. Or do you disagree with that verifiable fact? – David Heffernan Dec 30 '12 at 16:33
  • If there is only one field in each variant part, you are right. Otherwise, you are wrong. If the first is byte, word, dword, byte, and the second is array[0..7] of byte then they don't align to the same memory layout. – Rudy Velthuis Dec 30 '12 at 16:37
  • @RudyVelthuis and David: Please open a new question: This seems to be extremly interesting. – alzaimar Dec 30 '12 at 20:14
11

In XE3 there are record helpers for simple types, TDoubleHelper.

This works:

var
  d : Double;
  i : Integer;
begin
  d := 100.0;
  for i := 0 to 7 do
    WriteLn(d.Bytes[i]);
end;

In XE2 there is a declaration TDoubleRec, which is an advanced record.

example:

var
  dRec : TDoubleRec;
  i : Integer;
begin
  dRec := 100.0;
  for i := 0 to 7 do
    WriteLn(dRec.Bytes[i]);
end;

Another common option to access the bytes of a double is to use a typecast:

type
  TDoubleAsByteArr = array[0..7] of byte;
var
  d : Double;
  i : Integer;
begin
  d := 100.0;
  for i := 0 to 7 do
    WriteLn(TDoubleAsByteArr(d)[i]);
end;
LU RD
  • 34,438
  • 5
  • 88
  • 296
7

Two examples for using of "absolute" ...

Used as function

function GetDoubleByte(MyDouble: Double; Index: Byte): Byte;
var
  Bytes: array[0..7] of Byte absolute MyDouble;
begin
  Result := Bytes[Index];
end;



procedure TForm1.Button1Click(Sender: TObject);
var
 MyDouble:Double;
 DoubleBytes:Array[0..7] of Byte absolute MyDouble; // direct local use
begin
  MyDouble := 17.123;
  ShowMessage(IntToStr(DoubleBytes[0])); // local usage

  ShowMessage(IntToStr(GetDoubleByte(MyDouble,0))); // via function call

end;
bummi
  • 27,123
  • 14
  • 62
  • 101
  • 2
    Thank You. All Yours proposals opened my eyes for newer (in my life) appearance of Delphi. – Artik Dec 29 '12 at 14:49