当FPU控制字发生更改时,FloatToStr返回不精确的值

mca*_*a64 1 delphi floating-point

我使用Delphi 7现在FloatToStr(64)返回

64,0000017441

当在使用Direct3D代码的同一进程中加载​​不同的模块时.

返回值是

64

正如预期的那样,当其他模块在此过程中不活动时.

为什么FloatToStr()的输出会根据执行的其他不相关代码而改变,以及如何始终获得浮点值的可靠且一致的字符串表示?

FloatToStr()的行为差异可以在以下示例代码中看到:

{$APPTYPE CONSOLE}
program Project1;
uses
  SysUtils;
begin
  Writeln('FloatToStr(64) = ', FloatToStr(64));
  Set8087CW(Get8087CW and $FCFF);
  Writeln('FloatToStr(64) = ', FloatToStr(64));
end.
Run Code Online (Sandbox Code Playgroud)

Nin*_*rry 5

在外部插件代码中,在“ display.cpp”代码中

hr = d3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, 
        D3DCREATE_NOWINDOWCHANGES | D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE,
        &d3dpp, &d3ddev );
Run Code Online (Sandbox Code Playgroud)

添加标志“ D3DCREATE_FPU_PRESERVE”,使代码为:

hr = d3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, 
        D3DCREATE_NOWINDOWCHANGES | D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE | D3DCREATE_FPU_PRESERVE,
        &d3dpp, &d3ddev );
Run Code Online (Sandbox Code Playgroud)

如果省略此代码,则Direct 3D会更改FPU对当前线程使用的精度,并且您会看到不太精确的值。

另请参阅http://blogs.msdn.com/b/tmiller/archive/2004/06/01/145596.aspx

替代方法(当您不能或不想更改其他插件代码时)是在显示浮点值时使用显式格式。

使用

   s:= FormatFloat('0.##', x);
Run Code Online (Sandbox Code Playgroud)

代替

   s:= FloatToStr(X);
Run Code Online (Sandbox Code Playgroud)

这将始终将值四舍五入到小数点后两位,并且在小数点分隔符后不显示尾随零。当两位最高的数字为0时,它将不显示任何十进制分隔符。

另一种选择是让代码在单独的线程中运行,因为FPU精度设置是针对每个线程的。

另一种选择是执行

 Reset8087CW;
Run Code Online (Sandbox Code Playgroud)

在代码中调用FloatToStr()之前。但是,这可能会使Direct3D混乱并在那里产生错误。


小智 5

提供一个简单的控制台应用程序,复制您正在看到的行为,以及一些更详细的解释:

{$APPTYPE CONSOLE}
program Project1;
uses
  SysUtils;
begin
  Set8087CW(Get8087CW and $FCFF);
  Writeln('FloatToStr(64) = ', FloatToStr(64));
end.
Run Code Online (Sandbox Code Playgroud)

输出:

FloatToStr(64) = 64.000001744411

单步执行调试器,转换64为十进制表示的代码最终会调用

function _Pow10(val: Extended; Power: Integer): Extended;
Run Code Online (Sandbox Code Playgroud)

用val = 64,Power = 16计算64 * 10**16.然后将其转换为整数640000000000000000,从中提取十进制数字,并在其中放置小数点.这在默认的FPU模式下会很好,它会640000000000000000精确地产生,但在这里使用的FPU模式中,可用的精度较低,而不足以表示该数字的精度.它会向上舍入到最接近的可表示数字,并且后续代码正确地提取小数字会 看到最后的数字不是0.

这个问题是2004年未解决的QC报告的主题:QC#7275.

  • @NineBerry我认为这会折磨所有版本的Delphi.Mea culpa.我不应该假设Emba可以正确地编写浮点代码.我向大家道歉. (2认同)