Math precision bug with sine?


#1

Hi,

I was looking through the MSVC issues list and noticed an interesting test for math precision:

#include <iostream>
#include <math.h>

using namespace std;

int main()
{
   cout << sin(pow(2.0, 64)) << endl;
}

Running the binary generated using GCC-5.2 mingw64, I get the following results:

0.0235985

Running similar code using Python-2.7.10 also generates similar results:

import math
print math.sin(pow(2.0, 64))

Output:

0.0235985099044

However, with clang-3.6.2 and rustc-1.2.0 mingw64 the code generates the following results:

0.31282133

Rust code used for the test:

fn main()
{
    let val = 2.0_f32.powf(64.0).sin();
    println!("{}", val);
}

What should be the correct output? Is this a bug in LLVM?

Thanks, Jac


#2

Wolfram|Alpha says sin(2**64 rad) = 0.0235985 too. However, on my machine, I get the following:

  • Python 3.4.3 (32-bit, compiled with MSVC), sin(2.0**64) = 0.2472606463094177
  • Rust nightly (32-bit), 2.0f32.powf(64.0).sin() = 0.31282133
  • Rust nightly (32-bit), 2.0f64.powf(64.0).sin() = -0.35464734997014274
  • gcc 4.8.1 (32-bit), sin(powf(2.0, 64.0)) = 0.023599
  • gcc 4.9.0 (64-bit), sin(powf(2.0, 64.0)) = 0.023599
  • clang 3.4.2 (64-bit), sin(powf(2.0, 64.0)) = 0.312821
  • Windows 7 calc (:P), 2 ^ 64 sin = 0.27563735581699…

Going a bit further and looking at the generated machine code, the GCC version has this:

        ...
        movabsq $4582459028794800445, %rax
        ...

In constrast, the clang version has this:

        ...
        movss   .LCPI0_0(%rip), %xmm0
        movss   .LCPI0_1(%rip), %xmm1
        callq   powf
        cvtss2sd        %xmm0, %xmm0
        callq   sin
        ...

In other words, GCC is computing the result during compilation, whereas clang is computing it at runtime. Passing -O0 doesn’t appear to suppress this behaviour. Passing -O3 to clang causes it to emit this:

        ...
        movss   .LCPI0_0(%rip), %xmm0
        callq   exp2f
        cvtss2sd        %xmm0, %xmm0
        callq   sin
        ...

I wasn’t able to convince clang to produce the same code as GCC.

Edit Bonus round: let’s ask playbot on the IRC channel what it thinks:

- playbot: (2.0f32.powf(64.0).sin(), 2.0f64.powf(64.0).sin())
- (0.02359851, 0.023598509904439558)
- playbot: VERSION
- "rustc 1.2.0 (082e47636 2015-08-03)"

Playbot is running on x86_64 linux… what’s interesting is that it’s producing the above at runtime. So it’s ostensibly doing the same thing as clang on my machine… except it’s giving radically different results. Wat.

Having downloaded the latest 64-bit nightly, I get:

C:\Users\drk>rustc -vV
rustc 1.4.0-nightly (e5d90d984 2015-08-07)
binary: rustc
commit-hash: e5d90d98402475b6e154ce216f9efcb80da1a747
commit-date: 2015-08-07
host: x86_64-pc-windows-gnu
release: 1.4.0-nightly

C:\Users\drk>cargo script --expr "(2.0f32.powf(64.0).sin(), 2.0f64.powf(64.0).sin())"
   Compiling expr v0.1.0 (file:///C:/Users/drk/AppData/Local/Cargo/script-cache/expr-a58832b99c8d4fdd)
(0.31282133, -0.35464734997014274)

So… Rust is consistent on the same platform, but not on the same architecture. Wat?


#3

The test compiled with MSVC 64-bit produces the same output as GCC when executed. It is also no surprise that the MSVC 64-bit build of Rust produces accurate results in this case.


#4

Transcendental math functions like sin() come from the C runtime at the moment, and on Windows, those functions have issues. Among other things, some of the routines Rust needs don’t even exist on Windows. (glibc generally does much better.)