Delphi FormatFloat不同的行为(变量和数据集)

时间:2014-07-26 03:54:18

标签: delphi ado delphi-xe5 unidac

我发现了Delphi的FormatFloat函数的奇怪行为。让我展示案例研究。

要转换的价值:129809.495
格式化输出所需:129,809.50

案例1:从字符串转换

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

案例2:从双变量转换

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

案例3:从数据集的字段转换

*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

我一直在使用SQL Server 2008& amp ;; Firebird 1.5。
使用的组件是ADO组件和UniDAC组件(由DevArt提供),并且都具有相同的行为。

我试过这些:

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

但是所有导致同样错误的转换.49而不是.50

有效的方法是

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

有没有人有这种行为的解决方案?因为强制转换StrToFloat然后重新格式化变量/输出是太多的工作。此解决方法不能应用于使用FormatFloat

的第三方组件

任何帮助表示赞赏。感谢

2 个答案:

答案 0 :(得分:1)

看起来它与Double和Extended之间的某些精度差异有关。实际上我无法确认您对案例2的正确观察。也许是因为你将变量数声明为double,但是然后使用未知类型的变量val。

无论如何,以下代码

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;
使用XE6编译的

产生以下输出:

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

这导致得出结论:double不能保持正确的值以按预期舍入,而扩展更合适。此外,当值以double格式存储时,只需将其转换为扩展(即FormatFloat会发生这种情况)并不能解决舍入错误。

答案 1 :(得分:-1)

在这里创建一个单位MyUnit:

unit MyUnit;

interface
uses
  System.SysUtils;

function FormatFloat(const Format: string; Value: Extended): string;

implementation

function FormatFloat(const Format: string; Value: Extended): string;
begin
  Value := StrToFloat(Value.ToString);  
  Result:=System.SysUtils.FormatFloat(',0.00', Value);
end;

end.

并在Uses子句的最后位置 中插入 。对FormatFloat函数的任何调用都将调用您的函数。

顺便说一句,这种行为可能是四舍五入或准确度不足的结果。我试过这个:

var
  val: Single;

begin
  val:=129809.495;
  ShowMessage(FormatFloat(',0.00', val));

我的函数和rseult是val 129809.49,因为从Single转换为Extended后传递给函数的变量是129809.4921875。因此,对于调试写入每个函数调用的日志文件可能是个好主意。