? about Anatomy of a Double in general and in Delphi specifically

I understand that doubles have a sign (1 bit), exponent (11 bits) and fraction (52 bits) (according to Wikipedia anyway).

I have three questions.

1) How do I use these three parts to convert them to a number I can use (both the Integral and fractional parts)?

2) How are the three parts stored in a double within Delphi?

3) Once I know the integral and fractional parts, I'm really using them as a date and time with the date being the integral part being an offset in days and the fractional being an offset, I think, in milliseconds from midnight. So really its two numbers I am trying to derive from the double, days and milliseconds. The question is, is there a way to separate the two into ints? that is, day offset into int (or longint) and milliseconds into another int (or longint) using standard math functions?

I found out earlier that hex representations of dates I have stored were indeed Delphi doubles (see below). But I don't know how the double is represented

----------- answer to an earlier question --------------------------

Effectively these strings looks like the Hex representation for the Delphi TDatetime type which is an alias for the Double type (8 bytes), Where the integral part of the TDateTime value is the number of days that have passed since 12/30/1899 and the fractional part of the TDateTime value is fraction of a 24 hour day that has elapsed.

in Delphi you can parse such values using the HexToBin function like so

{$APPTYPE CONSOLE}

uses
  System.Classes,
  System.SysUtils;

function Decode(const HexStr: AnsiString) : TDateTime;
begin
   Assert(Length(HexStr)=16, 'Lenght must be 16');
   HexToBin(PAnsiChar(HexStr), @Result, SizeOf(TDateTime));
end;

begin
  try
     Writeln(DateTimeToStr(Decode('a297780add3ee440')));
     Writeln(DateTimeToStr(Decode('5c6c320bdd3ee440')));
     Writeln(DateTimeToStr(Decode('67b176e0dd3ee440')));
     Writeln(DateTimeToStr(Decode('38a7e155bc42e440')));
     Writeln(DateTimeToStr(Decode('d94ee458bc42e440')));
     Writeln(DateTimeToStr(Decode('d22af9134989dc40')));
     Writeln(DateTimeToStr(Decode('d4fb7863c542e440')));
     Writeln(DateTimeToStr(Decode('e501c962c542e440')));
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  ReadLn;
end.

This will return

07-07-2013 21:46:50
07-07-2013 21:46:58
07-07-2013 22:24:27
07-08-2013 21:15:05
07-08-2013 21:15:37
01-01-1980 3:24:15
08-08-2013 4:02:29
08-08-2013 4:02:21

Answers


A Delphi double value is a standard IEEE754 double, using the standard representation. All languages do this for various reasons. Not least of which is that the underlying hardware operates of IEEE754 types.

You can pull a double value apart using the TDoubleHelper introduced in XE3. The documentation shows you how: http://docwiki.embarcadero.com/Libraries/en/System.TDoubleHelper. For older Delphi versions you would use TDoubleRec instead.

Now, splitting the value into its significand and exponent will not help you. You want the integer part and the fractional part which are entirely different things. Get them using the standard functions Int() and Frac().

However, when you do this, you still won't have what you need. The fractional part is a floating point number between 0 and 1. That doesn't sound much like a number of milliseconds. So I believe that you do not yet fully understand this representation.

Perhaps what you have is nothing more than a TDateTime! In which case you use the standard functions from the RTL to decode it.


I've just read your earlier question. You are indeed on the wrong track. Read again the answer. Specifically:

Where the integral part of the TDateTime value is the number of days that have passed since 12/30/1899 and the fractional part of the TDateTime value is fraction of a 24 hour day that has elapsed.

So, in your VB code, convert the hex string, or binary, whichever you have, into a double. Then get the integer part and the fractional part and you are done..


You're misunderstanding what the relevant parts of a TDateTime contain.

The integral part (the part to the left of the decimal point) is the number of days that have elapsed since December 30, 1899, as you state. However, the fractional part (the part to the right of the decimal), is simply the decimal representation of the time that has passed since 00:00:00 0001 (the start of that day).

In other words,

0.25          12/30/1899 06:00:00 (6:00 AM)
0.5           12/30/1899 12:00:00 (noon)
0.75          12/30/1899 18:00:00 (6:00 PM)

The simplest way to work with Delphi's TDateTime and it's various parts is to let the RTL handle it for you. To separate out the individual portions of the time, use DecodeTime. To retrieve the individual parts of a date, use DecodeDate (both from the 'SysUtils' unit:

var
  Hr, Min, Sec: Word;
  Yr, Mon, Day: Word;  

  DecodeTime(Now, Hr, Min, Sec);
  DecodeDate(Now, Yr, Mon, Day);

To retrieve them all at once, use the DateUtils unit and DecodeDateTime instead:

  DecodeDateTime(Now, Yr, Mon, Day, Hr, Min, Sec);

If you really do want just the individual parts (the left and right sides of the decimal point), then as David says you just use Int and Frac


Need Your Help

Shortcut keys for menu items (alt + letter). Is there any convention for namings?

windows winforms user-interface

Is there any official/unofficial/informal convention for alt+key namings ? maybe, Microsoft have some internal document for that thing. Different menu levels and so on ...

JQuery.AJAX - can my server return a blob of data?

jquery ajax

For the dataType option to the JQuery.AJAX function, I don't see byte array or blob as one of the possibilities.