Question

I'm using FloatToText this way:

function ExFloatToStr(Value: Extended): string;
var
  Buffer: array[0..63] of Char;
  FormatSettings: TFormatSettings;
begin
  GetLocaleFormatSettings(GetUserDefaultLCID, FormatSettings);
  SetString(Result, Buffer, FloatToText(Buffer, Value, fvExtended, ffGeneral,
    18, 0, FormatSettings));
end;

If I pass to this function value 9229.99, it returns a string value 9229.9900000000016, but it's not what I want. When I create a new project and just copy the above code there, it works well. It returns 9229.99.

What might be the reason they work different in different projects ?

Was it helpful?

Solution

This is in fact an issue of representability. Here's the SSCCE:

{$APPTYPE CONSOLE}

uses
  SysUtils, Windows;

function ExFloatToStr(Value: Extended): string;
var
  Buffer: array[0..63] of Char;
  FormatSettings: TFormatSettings;
begin
  GetLocaleFormatSettings(GetUserDefaultLCID, FormatSettings);
  SetString(Result, Buffer, FloatToText(Buffer, Value, fvExtended, ffGeneral,
    18, 0, FormatSettings));
end;

var
  X: Double;
  Y: Extended;

begin
  X := 9229.99;
  Y := 9229.99;
  Writeln(ExFloatToStr(X));
  Writeln(ExFloatToStr(Y));
  Readln;
end.

Output

9229.98999999999978
9229.99

You ask for 18 digits. When you store the value as a double precision, the value stored becomes imprecise to 18 decimal digit precision. A double precision value has 15-16 significant decimal digits of precision. And that is fewer than 18. When you store the value as an extended value, there is more precision available, sufficient to store your value accurately to the 18 decimal digits of precision that your requested.

I always refer to Rob Kennedy's excellent page on this matter: http://pages.cs.wisc.edu/~rkennedy/exact-float?number=9229.99

This tells us that your value, when converted to extended and double is represented as:

9229.99 = + 9229.99000 00000 00000 21316 28207 28030 05576 13372 80273 4375
9229.99 = + 9229.98999 99999 99781 72127 15744 97222 90039 0625

And this tallies precisely with the output above.

So, I expect that you will find that in your existing project you will, at some point, be storing the value to a double precision variable. And at that point you lose the extra precision of Extended.

For what it is worth, I regard the 80 bit extended type as an anachronism. It is only supported on Intel chips, and only used by 32 bit compilers. In my experience it nevers offers any real benefit over double precision. And its performance is poor because of memory alignment. As the author of a lot of floating point code, I never use extended.

OTHER TIPS

There is a difference when compiling for x86 vs x64 Windows targets.

For x86 platform the 'Extended' type has a different 'size' then on the x64 platform.

See the Delphi help (XE2):

System.Extended offers greater precision than other real types, but is less
portable. Be careful using System.Extended if you are creating data files to
share across platforms. 

On Win32 systems, the size of System.Extended is 10 bytes. 

On Win64 systems, however, the System.Extended type is an alias for System.Double,
which is only 8 bytes. This difference can adversely affect numeric precision in
floating-point operations. For more information, see Delphi Considerations for
Cross-Platform Applications. 

Writeln(IntToStr(SizeOf(Extended))); // displays 10 on Win32 and 8 on Win64

Essentially this means on x64 platforms, you have less significant floating point values, which might result in a different conversion to a textual value.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top