铸造和分配真的会去除浮动的任何额外精度吗

Does casting and assignment really strip away any extra precision of floats?

本文关键字:任何额 精度 分配 真的      更新时间:2023-10-16

我正在阅读新的C++常见问题解答,我发现即使x == y用于double x, y;,也可能用于:

std::cos(x) == std::cos(y)

以评估为CCD_ 3。这是因为机器可以具有支持扩展精度的处理器,因此==的一部分是64位数字,而另一部分是80位数字。

然而,下一个例子似乎是不正确的:

void foo(double x, double y)
{
  double cos_x = cos(x);
  double cos_y = cos(y);
  // the behavior might depend on what's in here
  if (cos_x != cos_y) {
    std::cout << "Huh?!?n";  // You might end up here when x == y!!
  }
}

据我在en.cppreference.com上读到的内容:

铸造和赋值去掉了任何无关的范围和精度:这为存储扩展精度值的操作建模FPU寄存器到标准大小的存储器位置。

因此,分配:

double cos_x = cos(x);
double cos_y = cos(y);

应该去掉任何额外的精度,使程序完全可预测。

那么,谁是对的呢?C++常见问题解答或en.cppreference.com?

ISOCPP常见问题解答和cppreference都不是权威来源。cpprreference本质上相当于wikipedia:它包含很好的信息,但任何人都可以添加任何没有来源的内容——你必须谨慎阅读。以下三种说法正确吗:

你抓到了吗?您的特定安装可能会将其中一个cos()调用的结果存储到RAM中,在过程中截断它,然后稍后将截断的值与第二个cos(()调用未截断的结果进行比较。根据许多细节,这两个值可能不相等。

这是因为机器可以具有支持扩展精度的处理器,因此==的一部分是64位数字,而另一部分是80位数字。

Cast和赋值去掉了任何无关的范围和精度:这模拟了将扩展精度FPU寄存器中的值存储到标准大小的内存位置的操作。

也许吧。它取决于平台、编译器、编译器选项或任何数量的东西。实际上,上述语句仅适用于32位模式下的GCC,默认为传统x87 FPU(内部所有内容都是80位),存储在内存中时四舍五入为64位。64位使用SSE,因此双临时总是64位。可以使用mfpmath=sse-msse2强制SSE。无论哪种方式,组件都可能看起来像这样:

    movsd   QWORD PTR [rsp+8], xmm1
    call    cos
    movsd   xmm1, QWORD PTR [rsp+8]
    movsd   QWORD PTR [rsp], xmm0
    movapd  xmm0, xmm1
    call    cos
    movsd   xmm2, QWORD PTR [rsp]
    ucomisd xmm2, xmm0

正如您所看到的,这里没有任何类型的截断,也没有使用80位寄存器。

我希望赋值或强制转换能去掉额外的精度。我曾与"聪明"的编译器合作过,但这并没有发生。很多都是这样,但如果你想要真正独立于机器的代码,你需要做额外的工作。

通过用"volatile"关键字声明变量,可以强制编译器始终使用具有预期精度的内存中的值。

有些编译器在寄存器中而不是在堆栈中传递参数。对于这些编译器,xy可能具有意外的额外精度。为了完全安全,你可以做

volatile double  db_x = x,          db_y = y;
volatile double cos_x = cos(db_x), cos_y = cos(db_y);
if (cos_x != cos_y)...
相关文章: