0

Arithmetic result for the same expression lead to different outcomes depending on wether I define an integer in one line or I use several steps:

int main() {

    unsigned long long veryBigIntOneLine = ((255*256+255)*256+255)*256+255;

    unsigned long long veryBigInt = 255;
    veryBigInt *= 256;
    veryBigInt += 255;
    veryBigInt *= 256;
    veryBigInt += 255;
    veryBigInt *= 256;
    veryBigInt += 255;

    unsigned long long veryBigIntParanthesis = (((((255*256)+255)*256)+255)*256)+255;

    unsigned long long fourthInt = 256;
    fourthInt *= 256;
    fourthInt *= 256;
    fourthInt *= 256;
    --fourthInt;

    cout << "veryBigIntOneLine: " << veryBigIntOneLine << endl;
    cout << "veryBigInt: " << veryBigInt << endl;
    cout << "veryBigIntParanthesis: " << veryBigIntParanthesis << endl;
    cout << "fourthInt: " << fourthInt << endl;

    return 0;

}

they should all describe the same number, 256^4-1 (or 2^32-1), but the outcome is different.

veryBigIntOneLine: 18446744073709551615
veryBigInt: 4294967295
veryBigIntParanthesis: 18446744073709551615
fourthInt: 4294967295

4294967295 is the expected answer (as it is given for all four expressions by the Google calculator).

Also 18446744073709551615 is probably not an exact result of what is computed as I get an overflow warning at compilation time for both one line expressions (even when I tried with type __int128). It is actually 2^64-1, which is the max value for unsigned long long with my compiler (veryBigIntOneLine+1 gives 0).

Deduplicator
  • 44,692
  • 7
  • 66
  • 118

2 Answers2

2

Initialization code ((255*256+255)*256+255)*256+255 suffers from signed integer overflow which is Undefined Behavior, as well as from implicit conversion of signed int to unsigned. While step-by step calculations avoid those problems because right hand operand is implicitly converted to unsigned long long.

Simply using appropriate literals will fix those issues:

unsigned long long veryBigIntOneLine{ ((255ull*256ull+255ull)*256ull+255ull)*256ull+255ull}; // 4294967295
user7860670
  • 35,849
  • 4
  • 58
  • 84
  • Ok, thank you, `unsigned long long veryBigIntOneLine = (unsigned long long)((255*256+255)*256+255)*256+255;` works fine. – Julien Dhondt Apr 13 '19 at 16:17
  • And most compilers will warn about that, if prompted. Though adding `constexpr` changes that to an error which will always be diagnosed: http://coliru.stacked-crooked.com/a/5cc7d4a84abd756a As an aside, it would be enough to make *one* of the inner constants an `unsigned long long`, and depend on implicit promotions for the rest. – Deduplicator Apr 13 '19 at 16:18
  • @JulienDhondt You can simply work everywhere with unsigned long long by using appropriate literal suffix. Also notice use of list initialization - it prohibits narrowing conversion. – user7860670 Apr 13 '19 at 16:22
  • @Deduplicator So basically, this `unsigned long long veryBigIntOneLine = ((255ull*256+255)*256+255)*256+255;` should work? – Julien Dhondt Apr 13 '19 at 16:25
  • @JulienDhondt It would kinda work, but will still have a lot of implicit conversions of 255 to unsigned long long. – user7860670 Apr 13 '19 at 16:27
  • @VTT Thanks again ,I had never realized that there were literal suffixes and prefixes outside L"..." for wchar_t[]. – Julien Dhondt Apr 13 '19 at 16:28
  • 1
    @JulienDhondt You can also define your own suffixes. See user-defined literal – user7860670 Apr 13 '19 at 16:29
0

This is because you are not using unsigned long long literals. If you want literals to match your definitions you need to use:

255ULL + 256ULL * 255ULL + ...

The ULL is very important if you create numbers that are 64 bits. In C, without the suffix a number may be 64, 32 or even just 16 bits (even bytes on some CRAY where 64 bits. That also means your code would have worked just find on one of those CRAY systems.)

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
  • Thank you, the only literal specifier that I knew was L" for wchar_t[] strings. I didn't know that other kind of literal expressions were typed, processed/computed, and only after converted at definition/assignment. – Julien Dhondt Apr 13 '19 at 16:33
  • Side note: `int` is not necessarily 32 bit (and there are quite a number of architectures having 16-bit int!). – Aconcagua Apr 13 '19 at 16:52