【问题标题】:Why value of double seems to be changed after assignment?为什么分配后 double 的值似乎发生了变化?
【发布时间】:2014-03-24 16:50:03
【问题描述】:

以下程序的结果在我的机器上对我来说有点奇怪。

#include <iostream>

using namespace std;

int main(){
    double a = 20;
    double b = 0.020;
    double c = 1000.0;

    double d = b * c;

    if(a < b * c)
        cout << "a < b * c" << endl;

    if(a < d)
        cout << "a < d" << endl;

    return 0;
}

输出:

$ ./test
a < b * c

我知道由于精度,double 并不那么准确。但我不希望该值发生变化并给出不一致的比较结果。

如果a &lt; b * c 被打印出来,我确实希望a &lt; d 也应该被打印出来。但是当我在我的 i686 服务器甚至我的 cygwin 上运行这段代码时。我可以看到a &lt; b * c,但看不到a &lt; d

已确认此问题与平台有关。这是双重赋值的指令和实现方式不同造成的吗?

更新

生成的程序集:

main:
.LFB1482:
    pushl   %ebp
.LCFI0:
    movl    %esp, %ebp
.LCFI1:
    subl    $56, %esp
.LCFI2:
    andl    $-16, %esp
    movl    $0, %eax
    subl    %eax, %esp
    movl    $0, -8(%ebp)
    movl    $1077149696, -4(%ebp)
    movl    $1202590843, -16(%ebp)
    movl    $1066695393, -12(%ebp)
    movl    $0, -24(%ebp)
    movl    $1083129856, -20(%ebp)
    fldl    -16(%ebp)
    fmull   -24(%ebp)
    fstpl   -32(%ebp)
    fldl    -16(%ebp)
    fmull   -24(%ebp)
    fldl    -8(%ebp)
    fxch    %st(1)
    fucompp
    fnstsw  %ax
    sahf
    ja  .L3
    jmp .L2

    //.L3 will call stdout

【问题讨论】:

  • 可能跟不断优化有关。如果您从std::cin 中获取abc,您能否展示或复制它?
  • 编译器没有错误/警告。
  • 我可以复制它如果我从 std::cin 得到它,我稍后会发布程序集。 @NateKohl
  • 打印后确认值相同。 @vdbuilder
  • 我没有得到 x87 和 SSE 代码的输出。您可以在coliru.stacked-crooked.com/a/3022f77c07303e32查看结果并自己编辑代码您使用的是什么编译器?你的机器 AS3 到底是什么?

标签: c++ compiler-construction architecture


【解决方案1】:

假设:您可能会看到 80 位英特尔 FPU 的效果。

使用double d = b * c 的定义,b * c 以 80 位精度计算,并在存储到 d 时舍入为 64 位。 (a &lt; d) 会将 64 位 a 与 64 位 d 进行比较。

OTOH,使用表达式 (a &lt; b * c),在离开 FPU 之前,您有一个 80 位算术结果 b * c 直接与 a 进行比较。因此,b*c 结果永远不会因为保存在 64 位变量中而降低其精度。

您必须查看生成的指令以确定,我预计这会因编译器版本和优化器标志而异。

【讨论】:

  • 我尝试在这里设置 x87 coliru.stacked-crooked.com/a/3022f77c07303e32 但没有任何区别。 x87 或 SSE 没有输出。这并没有否定您所说的任何内容,但我只是好奇我必须设置哪些编译器标志才能看到效果。
【解决方案2】:

我不确定 AS3 机器是什么类型的硬件,但例如,您可以在内部浮点单元使用大于 64 位浮点数来存储中间结果的机器中看到这种行为。在带有 x87 浮点单元的 x86 架构中就是这种情况(但不是 SSE)。

问题是处理器会将bc 加载到浮点寄存器中,然后进行乘法运算并将临时结果存储在寄存器中。如果此寄存器大于 64 位,则结果将不同于计算并存储回内存的d(或a),强制它们为 64 位。

这是众多场景中的一种,您需要查看您的汇编代码以确定到底发生了什么。您还需要了解您的硬件如何在内部处理浮点计算。

【讨论】:

  • 我认为你是对的。但这是否意味着在比较 a 和中间结果时,中间值不会被放入内存,而是将 a 加载到 fpu 并在那里进行比较?
【解决方案3】:

在我的 Windows 机器上使用 MinGW 对代码进行快速测试会产生完全相同的结果。真正奇怪的是,如果我将双打更改为浮点数,一切都会正常运行(根本没有输出)。但是,如果我将它们更改为长双打,“a

我的猜测可能是因为双精度值应该允许更高的精度,当将两个立即值相乘并进行比较时会发生一些奇怪的事情,而不是存储结果供以后使用?这也可以解释为什么这个问题最终也会出现在 long doubles 上,因为它们需要更多的内存空间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-24
    • 2011-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多