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

时间:2016-01-30 19:39:48

标签: 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.

2 个答案:

答案 0 :(得分:5)

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

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

输出:

FloatToStr(64) = 64.000001744411

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

function _Pow10(val: Extended; Power: Integer): Extended;

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

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

答案 1 :(得分:4)

在外国插件代码中,在" display.cpp"在代码中

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

添加标志" D3DCREATE_FPU_PRESERVE"所以代码是:

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

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

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

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

使用

   s:= FormatFloat('0.##', x);

而不是

   s:= FloatToStr(X);

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

另一种方法是让你的代码在一个单独的线程中运行,因为FPU精度设置是每个线程。

另一种选择是执行

 Reset8087CW;
在调用FloatToStr()之前,在代码中

。但是,这可能会使Direct3D混乱并在那里产生错误。