【问题标题】:Floating point computation changes if stored in intermediate "double" variable如果存储在中间“双”变量中,浮点计算会发生变化
【发布时间】:2015-03-29 23:00:18
【问题描述】:

我正在尝试编写一个简单的 log base 2 方法。我知道在计算机上表示 std::log(8.0) 和 std::log(2.0) 之类的东西很困难。我也了解 std::log(8.0) / std::log(2.0) 可能会导致值略低于 3.0。我不明白的是,与直接转换公式相比,为什么将下面的计算结果放入 double 并将其转换为左值然后将其转换为 unsigned int 会改变结果。以下代码显示了我的测试用例,它在我的 32 位 debian wheezy 机器上反复失败,但在我的 64 位 debian wheezy 机器上反复通过。

#include <cmath>
#include "assert.h"

int main () {
  int n = 8;
  unsigned int i =
    static_cast<unsigned int>(std::log(static_cast<double>(n)) /
                              std::log(static_cast<double>(2)));
  double d =
    std::log(static_cast<double>(n)) / std::log(static_cast<double>(2));
  unsigned int j = static_cast<unsigned int> (d);
  assert (i == j);
}

我也知道我可以使用位移来以更可预测的方式得出我的结果。我很好奇为什么将导致操作的双精度数与将该值粘贴到堆栈上的双精度数并将双精度数投射到堆栈上有何不同。

【问题讨论】:

  • x87 内部使用 80 位算法。许多编译器在针对 x87 时,只是假装那些额外的位不存在。 x87 上的这种丑陋是常见的结果。告诉编译器使用 SSE 而不是 x87,例如 -mfpmath=sse
  • 与@tmyklebu 的评论相吻合:32 位 x86 系统的工具链通常默认以 x87 FPU 为目标进行浮点计算,而 64 位系统的工具链通常默认为使用 SSE。超精度、双舍入等的组合会导致您在此处观察到的那种效果。

标签: c++ floating-point double floating-accuracy


【解决方案1】:

在 C++ 中,浮点是允许做这种事情的。

一种可能的解释是,除法的结果在内部以比double 更高的精度计算,并存储在一个比double 精度更高的寄存器中。

将其直接转换为 unsigned int 与首先将其转换为 double 然后再转换为 unsigned int 的结果不同。

要确切了解发生了什么,查看编译器为 32 位情况生成的程序集输出可能会有所帮助。

不用说,您不应该编写依赖于浮点运算准确性的代码。

【讨论】:

  • 在更好的编译器上,将结果存储为 long double 会产生预期的结果,因为该类型将在使用 64 位中间结果的机器上存储 64 位和 80 位(可能还有一些填充) 在使用 80 位中间结果的机器上。不幸的是,许多使用 printf 之类的代码无法正确区分 %f%lf 格式说明符,如果 long double 值以 80 位传递,则会中断; MS 决定最简单的解决方法是让 long double 为 64 位,即使在使用 80 位计算中间计算的机器上也是如此。
  • %f%lf 被定义为相同的。长双一是%Lf
  • 我的观点仍然是许多使用long double 的代码没有正确指定格式字符串,一些编译器供应商已经决定解决方案是让long double 在任何地方都是64 位,即使这完全破坏了该类型的语义。
猜你喜欢
  • 1970-01-01
  • 2016-03-21
  • 2020-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-27
  • 2021-10-23
  • 2018-03-16
相关资源
最近更新 更多