Lazarus

Free Pascal => General => Topic started by: cov on March 14, 2014, 10:50:56 pm

Title: How do you read Doubles from a byte array
Post by: cov on March 14, 2014, 10:50:56 pm
Sorry for asking such a dumb question, but I'm reading an eight byte double from a file.

Blockread(SHPFile, buffer[0], 8);

Is there a simple way to typecast the buffer?

I know that the eight bytes in the buffer are a double.

Surely this should be quite common circumstance, but I can't seem to find it on Google.

I'm about to start using this:

Code: [Select]

function TForm1.readDouble(buff: array of byte): Real;
var
  man, val: Int64;
  i, sh, expon, sign: integer;
begin
  man:=buff[0];
  sh:=8;
  for i:=1 to 5 do
  begin
    val:=buff[i];
    val:=val shl sh;
    man+=val;
    Inc(sh,8);
  end;
  val:=((buff[6]and 15)+16) shl 48;
  man+=val;
  expon:=((buff[7]and 127)shl 4)+ (buff[6]shr 4)-1075;
  Result:=power(2,expon)*man;
  if buff[7]>127 then Result*=-1;
end;   
Title: Re: How do you read Doubles from a byte array
Post by: Cyrax on March 15, 2014, 03:27:36 am
In which format is that eight byte double which are you trying to read from the file?

If it is in standard format, this code snippet should work:
Code: [Select]
Var
  ADouble : Double;
begin
   ..
   BlockRead(AFile, ADouble, SizeOf(ADouble));
   ..
end;

Supported Real types : http://www.freepascal.org/docs-html/ref/refsu6.html
Title: Re: How do you read Doubles from a byte array
Post by: Leledumbo on March 15, 2014, 04:22:52 am
Quote
I know that the eight bytes in the buffer are a double.

Surely this should be quite common circumstance, but I can't seem to find it on Google.
IEEE 754 doesn't specify endianness for floating point, so even if you know these 8 bytes are double, the byte order is unknown.
It's not common, especially in Pascal world, to do such a thing unless the file is saved with the same Pascal code compiled by the same Pascal compiler or it's manually saved with a format defined by the programmer.
Title: Re: How do you read Doubles from a byte array
Post by: engkin on March 15, 2014, 05:00:00 am
I think I have to re-read Leledumbo's answer a few times to understand what he meant. Meanwhile, I assume you are just like me, using Double as specified by your FPU (http://www.website.masmforum.com/tutorials/fptute/fpuchap2.htm#real8). Your code supports my assumption:
Code: [Select]
man :=
       (buff[0] shl 0) +
       (buff[1] shl 8) +
       (buff[2] shl 16) +
..
       (buff[5] shl 40) +
       ((buff[6]and 15)+16) shl 48;
in this case I think you can simply follow Cyrax' code.
Title: Re: How do you read Doubles from a byte array
Post by: cov on March 20, 2014, 03:51:54 pm
Thanks all, for your responses.

I was thinking that a pointer to the variable in the buffer array would be the way to go.

However, the file format ('.shp') mixes both endian formats, so it's safer to control this by shl as suggested by engkin.

My readDouble() function seems to work ok for 8-byte doubles and I've written this for other number formats:

Code: [Select]
function readInteger(buff: array of byte; pos,size: integer): integer;
var
  i,val,sh, endpos, interv: integer;
begin
  sh:=0;
  Result:=0;
  interv:=1;
  endpos:=pos+size;
  i:=pos;
  if size<0 then
  begin
    interv:=-1;
    endpos:=pos;
    i:=pos-size-1;
  end;
  while i<>endpos do
  begin
    val:=buff[i];
    Result+=val shl sh;
    Inc(sh,8);
    i+=interv;
  end;
end; 

A four byte big-endian integer occurring in the buffer at byte 4 would be read like this: myInt:=readInteger(buffer,4,4);

and a four byte little-endian integer occurring in the buffer at byte 4 would be read like this: myInt:=readInteger(buffer,8,-4);
Title: Re: How do you read Doubles from a byte array
Post by: engkin on March 20, 2014, 08:12:19 pm
However, the file format ('.shp') mixes both endian formats, so it's safer to control this by shl as suggested by engkin.
If you mean by '.shp' shapefiles then, based on ESRI Shapefile Technical Description, you know exactly the endianness of each field. You can benefit from a set of functions that convert between native and BE/SE order. BEtoN and SEtoN when reading shapefiles. And their counterparts, NtoBE and NtoSE, while writing shapefiles. All of these functions rely on SwapEndian.

For the examples you gave:
Code: [Select]
var
  b4: DWord; //4 bytes integer
...
//A four byte big-endian integer occurring in the buffer at byte 4
  b4 := BEtoN(PDWord((@Buffer[4]))^);
...
//a four byte little-endian integer occurring in the buffer at byte 4
  b4 := LEtoN(PDWord((@Buffer[4]))^);

Although the previous functions meant to deal with integer types, it is easy to typecast the result or even better to use absolute when declaring variables:
Code: [Select]
var
  b8: QWord; //8 bytes integer
  d8: Double absolute b8; //8 bytes double
...
  b8 := LEtoN(PQWord(@buffer[x])^);
  //d8 has the value in native endianness
that is based on the assumption that both the CPU and the FPU use the same endianness (which might not be true in general).
Title: Re: How do you read Doubles from a byte array
Post by: marcov on March 20, 2014, 09:51:10 pm

that is based on the assumption that both the CPU and the FPU use the same endianness (which might not be true in general).

It might also fix alignment. Some architectures (like PPC 603) require float alignment.
TinyPortal © 2005-2018