1

Using arithmetic on a mix of uint64_t and long produces unwanted results on arm (c++ compiler). Same code works as intended on x86.

If long is replaced with uint64_t, it works as expected.

Armv7 compiler is c++ (Debian 6.3.0-18+deb9u1) 6.3.0 20170516

Code here also: http://cpp.sh/2xrnu

int main()
{
    uint64_t x = 1000UL * 60 * 60 * 24 * 31;
    int i1 = 31;
    long l2 = 1000 * 60 * 60 * 24;
    uint64_t u2 = 1000 * 60 * 60 * 24;
    std::cout << "x        : " << x << std::endl;
    std::cout << "i1       : " << i1 << std::endl;
    std::cout << "l2       : " << l2 << std::endl;
    std::cout << "u2       : " << u2 << std::endl;
    std::cout << "x - i1*l2: " << x - i1 * l2 << std::endl; // expected '0', got 4294967296
    std::cout << "x - i1*u2: " << x - i1 * u2 << std::endl; // expected and got '0'
    return 0;
}

I expected the last two lines to give '0'.

On x86, the result is

i1       : 31
l2       : 86400000
u2       : 86400000
x - i1*l2: 0
x - i1*u2: 0

On Arm (CortexA8), the result is

i1       : 31
l2       : 86400000
u2       : 86400000
x - i1*l2: 4294967296
x - i1*u2: 0
Aksel
  • 75
  • 7
  • 1
    This expression 1000 * 60 * 60 * 24; has the type int. This expression 1000UL * 60 * 60 * 24 * 31; has the type unsigned long. – Vlad from Moscow Aug 22 '19 at 13:53
  • 1
    What is `sizeof(int)` and `sizeof(long)` on that platform? – Slava Aug 22 '19 at 13:54
  • @vlad-from-moscow Even if I change 1000 * 60 * 60 * 24 to 1000L * 60 * 60 * 24, the problem remains. And 1000 * 60 * 60 * 24 is 86400000, well below int max anyway? – Aksel Aug 22 '19 at 13:55
  • @Slava I got:```sizeof(int) :4 sizeof(long) :4 sizeof(uint64_t):8 ``` – Aksel Aug 22 '19 at 13:57
  • 2
    `31 * 86400000` does not fit into 31 bits... You're looking at UB from integer overflow. – Max Langhof Aug 22 '19 at 14:00
  • Thanks @MaxLanghof, that's it. Casting one of the 4 byte operators to 8 bytes does it. ```x - ((uint64_t)i1) * l2``` -> 0 – Aksel Aug 22 '19 at 14:02
  • 1
    Better do `static_cast()` it is safer and more readable. – Slava Aug 22 '19 at 15:34

1 Answers1

5

In this line of code:

std::cout << "x - i1*l2: " << x - i1 * l2 << std::endl; // expected '0', got 4294967296

when you multiply 31 by 86400000 you get 2678400000 which is 0x9FA52400 and it cannot fit to signed long of 4 bytes (sign bit is set to 1). Then you get UB due to signed overflow and garbage value converted to uint64_t to subtract it from x. On x86 you obviously have bigger long hense you do not see the issue.

Slava
  • 43,454
  • 1
  • 47
  • 90
  • Yes, that is it. I totally oversaw that intermediate result. Thanks Slava and @MaxLanghof – Aksel Aug 22 '19 at 14:04