12

The result of the following program is a little bit strange to me on my machine.

#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;
}

Output:

$ ./test
a < b * c

I know double is not that accurate because of the precision. But I don't expect that value changed and give an inconsistent comparison result.

If the a < b * c get printed out, I do expect that a < d should also get printed. But when I run this code on my i686 server and even on my cygwin. I can see a < b * c but cannot see a < d.

This issue has been confirmed to be platform dependent. Is this caused by the different instruction and implementation of double assignment?

UPDATE

The generated assembly:

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
StarPinkER
  • 14,081
  • 7
  • 55
  • 81
  • 1
    Perhaps it has something to do with constant optimization. Can you show the assembly or reproduce it if you grab `a`, `b`, and `c` from `std::cin`? – Nate Kohl Feb 23 '14 at 03:35
  • No errors/warnings from compiler. – StarPinkER Feb 23 '14 at 03:48
  • I can reproduce it If I get it from std::cin, I'll post the assembly later. @NateKohl – StarPinkER Feb 23 '14 at 03:51
  • The value is confirmed to be same after printing. @vdbuilder – StarPinkER Feb 23 '14 at 04:00
  • I get no output for x87 and SSE code. You can see the results and edit the code yourself at http://coliru.stacked-crooked.com/a/3022f77c07303e32 What compiler are you using? What exactly is your machine AS3? – Z boson Feb 23 '14 at 07:19
  • Maybe I'm just missing it, but I see two calls to `std::cout` with no branching in the assembly you posted. Are you sure that produces only one of the outputs? – Nate Kohl Feb 23 '14 at 13:42
  • It's my mistake, I printed out b*c and d. so you see two std::out. Actually there is no std::cout in this piece of code. The call is in .L3 segment. @NateKohl – StarPinkER Feb 23 '14 at 14:11

3 Answers3

5

Hypothesis: you may be seeing the effects of the 80-bit intel FPU.

With the definition double d = b * c, the quantity b * c is computed with 80-bit precision and rounded to 64-bits when it is stored into d. (a < d) would be comparing the 64-bit a to 64-bit d.

OTOH, with the expression (a < b * c), You have an 80-bit arithmetic result b * c being compared directly against a before leaving the FPU. So the b*c result never has its precision clipped by being saved in a 64-bit variable.

You'd have to look at the generated instructions to be sure, and I expect this will vary with compiler versions and optimizer flags.

dyp
  • 38,334
  • 13
  • 112
  • 177
Billy Donahue
  • 564
  • 2
  • 10
  • I tried setting x87 here http://coliru.stacked-crooked.com/a/3022f77c07303e32 but it made no difference. I get no output with x87 or SSE. That does not negate anything you said but I'm just curious what compiler flags I have to set to see the effect. – Z boson Feb 23 '14 at 19:47
3

I'm not sure what type of hardware an AS3 machine is, but for example, you can see this behavior in machines where the internal floating point unit uses larger than 64-bit floats to store intermediate results. This is the case in the x86 arch with the x87 floating point units (but not with SSE).

The problem is that processor will load in b and c to floating point registers, then do the multiplication and store the temporary result in a register. If this register is bigger than 64-bits, the result will be different than d (or a) which were computed and stored back to memory, forcing them to be 64-bits.

This is one scenario of many, you would need to look at your assembly code to determine exactly what is going on. You also need to understand how your hardware deals with floating point computations internally.

  • I think you are correct. But does it mean when comparing a and the intermediate result, the intermediate value won't be put to memory but loading a to the fpu and performing comparison there? – StarPinkER Feb 23 '14 at 05:40
1

A quick test of the code with MinGW on my Windows machine produces these exact same results. What's really strange though is that if I change the doubles to floats, everything runs perfectly fine as it should (no output at all). However, if I change them to long doubles, both "a < b * c" and "a < d" appear.

My guess is maybe since doubles are supposed to allow for more precision, something weird is going on when multiplying the two immediate values and doing a comparison, versus storing the result for later? That would also explain why eventually the issue shows up with long doubles too since they would require even more memory space.

GEMISIS
  • 434
  • 3
  • 11