delphiadodelphi-xe5unidac

Delphi FormatFloat different behavior (variable & dataset)


I have found a strange behavior of Delphi's FormatFloat function. Let me show the case study.

value to be converted : 129809.495
formatted output desired : 129,809.50

Case 1 : Converting from a string

var str: string;
str := '129809.495';  
str := FormatFloat(',0.00', StrToFloat(str));  
// output is 129,809.50 = CORRECT  

Case 2 : Converting from a double variable

var number: double;
str: string;
val := 129809.495;  
str := FormatFloat(',0.00', val);  
// output is 129,809.50 = CORRECT

Case 3 : Converting from a dataset's field

*it's too complex to write here, let me just explain*  
Basically the formatted output of FormatFloat(',0.00', Dataset.Field[0].AsFloat);
always resulted in 129,809.49 == WRONG

I've been testing this behavior using SQL Server 2008 & Firebird 1.5.
Component used are ADO Components and UniDAC components (by DevArt), and all have the same behavior.

I've tried to do these :

But all resulted in same wrong conversion .49 instead of .50

1 way that works is

val := StrToFloat(Dataset.Field[0].AsString); 
FormatFloat(',0.00', val);  

Has anyone got a solution for this behavior? Because it'd be too much work to force convert StrToFloat and then reformat the variable/output. And this workaround can't be applied to 3rd party components that were using FormatFloat

Any help appreciated. Thanks


Solution

  • It looks like it is related to some precision differences between Double and Extended. Actually I cannot confirm your observation about case 2 being correct. Perhaps because you declare a variable number as double, but then use a variable val of unknown type.

    Anyway, the following code

    var
      d: Double;
      e: Extended;
    begin
      d := 129809.495;
      e := 129809.495;
      Writeln(FormatFloat(',0.00', d));
      Writeln(FormatFloat(',0.00', e));
      e := d;
      Writeln(FormatFloat(',0.00', e));
      e := 129809.495;
      d := e;
      Writeln(FormatFloat(',0.00', d));
    end;
    

    compiled with XE6 produces the following output:

    129,809.49
    129,809.50
    129,809.49
    129,809.49
    

    This leads to the conclusion that a double is not able to hold the correct value to be rounded as expected, while an extended is more suitable. In addition, when the value is once stored in double format, simply converting it to extended (that is what happens with FormatFloat) doesn't heal the rounding error.