Search code examples
delphidoubleieee-754

StrToFloat and who is wrong: Delphi or ASE/SQL Server


Recently I found strange thing: result of

var
  d: double;
begin
  d := StrToFloat('-1.79E308');

is not the same as string value '-1.79E308' converted to float field type by ASE and SQL Server through

INSERT INTO my_table (my_float_field) VALUES (-1.79E308)

For Delphi memory dump is 9A BB AD 58 F1 DC EF FF
For ASE/SQL Server value in packet on select is 99 BB AD 58 F1 DC EF FF.

Who is wrong, both servers or Delphi?


Solution

  • The premise that we are working from is that StrToFloat yields the closest representable binary floating point value to the supplied decimal value.

    The two hexadecimal values the you present are adjacent. You can see that they differ by 1 in the significand. Here is some Python code that decodes the two values:

    >>> import struct
    >>> struct.unpack('!d', 'ffefdcf158adbb9a'.decode('hex'))[0]
    -1.7900000000000002e+308
    >>> struct.unpack('!d', 'ffefdcf158adbb99'.decode('hex'))[0]
    -1.79e+308
    

    Bear in mind that Python prints floating point values using the shortest possible significant for which the closest representable value is the actual value. That ffefdcf158adbb99 decodes to a value the prints as -1.79e+308 in the eyes of Python, is sufficient proof that ffefdcf158adbb99 is the closest representable value. In other words, the Delphi code is giving the wrong answer.

    And, just out of curiosity, in the opposite direction:

    >>> hex(struct.unpack('<Q', struct.pack('<d', float('-1.79e308')))[0])
    '0xffefdcf158adbb99L'
    

    It is interesting to note that the 32 bit Delphi compiler yields ffefdcf158adbb99 but the 64 bit Delphi compiler yields ffefdcf158adbb9a. This is a clear defect, and should be submitted as a bug report to Quality Portal.