casting uint32_t to uint64_t results in different value?
Asked Answered
D

2

14

Using Visual Studio 2015 C++, 14.0.25431.01 Update 3. I have unexpected behavior in my code. Compile and run with 64bit, Release:

#include <iostream>
#include <stdint.h>

int main(int, char**) {
    for (uint32_t i = 1; i < 3; ++i) {
        uint32_t a = i * 0xfbd1e995;
        uint64_t b = a;

        std::cout << a << " 32bit" << std::endl;
        std::cout << b << " 64bit" << std::endl;
    }
}

I expect that a and b have the same value, but when I run this I get this output:

4224838037 32bit
4224838037 64bit
4154708778 32bit
8449676074 64bit

It looks like the compiler replaces the 32bit multiplication with a 64bit multiplication. Is it allowed to do that, or is this a compiler bug? Both g++ and clang give me the numbers that I'd expect.

EDIT: I've update my code with a simpler version that has the same problem. Also, I've just submitted a bug report.

Doughman answered 28/2, 2017 at 14:33 Comment(10)
No repro with gcc and clang. This absolutely should not happen. Edit: Also no repro with VS on rextester.Gold
Can't reproduce with mingw and visual studio compiler as well.Archaeo
what version of VS are you using? Did you compile Release 64bit?Doughman
Same strange results witch Studio 2010 x64 ReleaseEustacia
"Is it allowed to do that, or is this a compiler bug?" That would be an extremely serious compiler bug. Are you sure that code is really what you are running?Gold
I could, as well, reproduce the issue, when using VS2013, and compiling in release mode.Abreu
Compiling with /O2 or /Ox results in the strange behavior, /Od or /O1 produces the expected outputEustacia
Looks like a bug to me. I think you should report it.Opinicus
I've just filed a bug reportDoughman
Only happens on Release build x64. The result is correct for Debug build, or Release build x86Supra
T
7

I could reproduce this on VS2010, and the immediate cause is this:

add ebx, 5BD1E995h  ; this is x
add rdi, 5BD1E995h  ; this is a 64bit version of x

Since it's a 64bit addition, it will just carry into the high 32 bits. This at least makes more sense than conjuring up a 64bit multiplication, it might be a corner case in induction variable elimination but that's just speculation.

Also fun is that it doesn't even save a cast by miscompiling it. The correct value is right there in rbx.

Thaumaturgy answered 28/2, 2017 at 14:53 Comment(2)
Why does it use add for a multiplication? Anyway it doesn't seem to appear in CL19 anymore. I can only find imul with 1540483477Monumental
I think the compiler tries to be smart and replaces the loop from 0 to 50, where loop counter is multiplied by the constant, with just adding the constant.Doughman

© 2022 - 2024 — McMap. All rights reserved.