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

Delphi FormatFloat不同的行为(变量和数据集),delphi,ado,delphi-xe5,unidac,Delphi,Ado,Delphi Xe5,Unidac,我发现Delphi的FormatFloat函数有一个奇怪的行为。让我展示一下案例研究 要转换的值:129809.495 所需格式化输出:129809.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:

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

要转换的值:129809.495
所需格式化输出:129809.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和Firebird 1.5测试这种行为。
使用的组件是ADO组件和UniDAC组件(由DevArt提供),它们都具有相同的行为

我尝试过这样做:

  • FormatFloat(',0.00',Dataset.Field[0].AsFloat)
  • FormatFloat(',0.00',StrToFloat(Dataset.Field[0].AsString))
  • val:=数据集。字段[0]。AsFloat;格式浮点(',0.00',val)
  • str:=数据集。字段[0]。关联字符串;FormatFloat(',0.00',StrToFloat(str))
  • val:=StrToFloat(Dataset.Field[0].AsString);格式浮点(',0.00',val)
但所有这些都导致了相同的错误转换。49而不是0.50

一个有效的方法是

val := StrToFloat(Dataset.Field[0].AsString); 
FormatFloat(',0.00', val);  
有人能解决这种行为吗?因为强制转换StrToFloat,然后重新格式化变量/输出,工作量太大了。这种变通方法不能应用于使用FormatFloat的第三方组件


谢谢你的帮助。谢谢

在这里制作一个我喜欢的单元:

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));

对于我的函数,结果是val 129809.49,因为在从单变量转换为扩展变量后,传递给函数的变量是129809.4921875。因此,对于调试而言,写入每个函数调用的日志文件可能是个好主意。

这似乎与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不能像预期的那样保持要舍入的正确值,而extended更合适。此外,当值曾经以双精度格式存储时,简单地将其转换为extended(FormatFloat就是这种情况)并不能解决舍入错误。

使用现有函数名是一种非常糟糕的做法。另外,您没有使用
格式
参数…不是混合而是过度使用。这是我找到的最快最简单的解决方案。如果用户愿意,他可以用Format参数覆盖函数。考虑到这样的函数名会发生什么,你永远不会称它为最快和最简单的函数(我知道你用这些最高级的词来描述代码)。如果您真的想解决OP的问题,您最好使用
t字段的类帮助程序。感谢您的回答,但我恐怕无法将此用于DevExpress的TcxCurrencyEdit组件及其显示格式设置。我先试试这个字段的数据类型是什么?@uwerabe Firebird 1.5的数据类型是“双精度”,SQLServerWow的数据类型是“金钱”,现在我知道了。但是如何将Dataset.Field.AsFloat转换为extended?正如其中所述。AsFloat返回double:(进行了更多的研究,发现因此我需要执行Field.AsCurrency以产生正确的精度舍入