Unexpected output for long data type in C++

Here is my code,

#include <bits/stdc++.h>

using namespace std;
int main(){
    int a;
    long b;
    cin >> a >> b;
    cout << b;
    return 0;
}

Output:

5 252525252525
2147483647
Process returned 0 (0x0)   execution time : 9.365 s
Press any key to continue.

Why 2147483647, i’m expecting 252525252525 as value of b?

I’m not sure what else you might have going on, but when I run your code in isolation, I see the expected value printed out:

1 Like

2147483647 == 2^31-1.
Meaning - your long is taking data from signed 32-bit int or there is some int assignment going on before your value is assigned to b variable.

Why is that happening? No idea. Try to cin on a separate line, with a different compiler, maybe with a different shell?

2 Likes

I’d guess this depends upon the compiler. It looks like you are taking a user input, saving it into an int, then saving that into a long. Which means that you can only store something as big as an int unless the compiler chooses to help you and behave in some non-standard way.

Edit: I was not right about the root issue, see my big reply in the 9th post

2 Likes

I’m using codeblocks-20.03mingw-setup


My OS,
3

I don’t know, how to fix this(already tried reinstall, change the compiler). Still facing the same issue :roll_eyes:
While using online ide, it works fine.

Hello there,

I have edited your post to remove the link to the executable. For everyone’s safety, we do not allow links to executables.

Thank you, for your understanding.

1 Like

You fix this by not putting the result into an int before putting the result into a long. Why are you taking the input (cin), placing it into an int (int a), and then placing it into a long (long b)? Why not directly place the result into b?

Each variable type can only represent values up to a certain size. An int has a lower maximum value than a long. See:
https://www.tutorialspoint.com/c_standard_library/limits_h.htm

INT_MAX +2147483647

See your magic number from above?

The fact that it happens to work for one compiler in one case is just a matter of luck. In general, this shouldn’t work.

Putting a value that is larger than the maximum possible for a variable type into the variable is undefined behavior, which means the C standard does not describe what must happen in this case. The behavior can differ between compilers or even the same compiler with different compilation options.

Edit: I was not right about the root issue, see my big reply in the 9th post

2 Likes

I believe this issue is because the size of a long on Windows is (by Microsoft convention) 32-bits, even on 64-bit builds. Try setting the type to long long or std::int64_t and see if that gives you the correct result.

Citation: Built-in types (C++) | Microsoft Docs.

There is no ‘C on Windows’ size that every C compiler uses just for Windows. Each compiler has different behavior. OP is using GCC, but that reference is for the MSVC compiler.


The sizes of each type are specified by the C and C++ standards.

Per the C standard, an int will be at least 16 bits and a long will be at least 32 bits, but most compilers use 32 bit ints and 64 bit longs on 64 bit systems.

In any case, 252525252525 is bigger than any int can represent, so that value cannot be stored in an int.

Its pretty straightforward to verify the maximum int and long for any system:

C/C++ Code to Check Limits
#include <stdio.h>
#include <limits.h>

int main() {
  printf("Max int: %d\n", INT_MAX);
  printf("Max long: %lu\n", LONG_MAX);
}

Though according to GodBolt, the assembly code that GCC emits should cast the input string directly as a long without the intermediate step of casting as an int, so that’s not an issue. (I didn’t expect that, but I suppose it makes sense that GCC does this, though I’m not sure why one would want to save an input as both an int and a long.)

C++ Code
#include <iostream>

using namespace std;
int main(){
    int short_input;
    long long_input;

    // Read user input
    cout << "Enter a number: ";

    cin >> short_input >> long_input;

    // Output result
    cout << "\nint: " << short_input << "\n";
    cout << "long: " << long_input << "\n";

    return 0;
}

Assembly via GodBolt

Assembly
.LC0:
        .string "Enter a number: "
.LC1:
        .string "\nint: "
.LC2:
        .string "\n"
.LC3:
        .string "long: "
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     esi, OFFSET FLAT:.LC0
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
        lea     rax, [rbp-4]
        mov     rsi, rax
        mov     edi, OFFSET FLAT:_ZSt3cin
        call    std::basic_istream<char, std::char_traits<char> >::operator>>(int&)
        mov     rdx, rax
        lea     rax, [rbp-16]
        mov     rsi, rax
        mov     rdi, rdx
        call    std::basic_istream<char, std::char_traits<char> >::operator>>(long&)
        mov     esi, OFFSET FLAT:.LC1
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
        mov     rdx, rax
        mov     eax, DWORD PTR [rbp-4]
        mov     esi, eax
        mov     rdi, rdx
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     esi, OFFSET FLAT:.LC2
        mov     rdi, rax
        call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
        mov     esi, OFFSET FLAT:.LC3
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
        mov     rdx, rax
        mov     rax, QWORD PTR [rbp-16]
        mov     rsi, rax
        mov     rdi, rdx
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(long)
        mov     esi, OFFSET FLAT:.LC2
        mov     rdi, rax
        call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
        mov     eax, 0
        leave
        ret
__static_initialization_and_destruction_0(int, int):
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], edi
        mov     DWORD PTR [rbp-8], esi
        cmp     DWORD PTR [rbp-4], 1
        jne     .L5
        cmp     DWORD PTR [rbp-8], 65535
        jne     .L5
        mov     edi, OFFSET FLAT:_ZStL8__ioinit
        call    std::ios_base::Init::Init() [complete object constructor]
        mov     edx, OFFSET FLAT:__dso_handle
        mov     esi, OFFSET FLAT:_ZStL8__ioinit
        mov     edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
        call    __cxa_atexit
.L5:
        nop
        leave
        ret
_GLOBAL__sub_I_main:
        push    rbp
        mov     rbp, rsp
        mov     esi, 65535
        mov     edi, 1
        call    __static_initialization_and_destruction_0(int, int)
        pop     rbp
        ret

So I would guess that yeah, this particular version of GCC was compiled for or is only targeting 32 bits. That’s not usual for GCC in my experience.


The typical values can been seen in GodBolt:

Assembly for limits
.LC0:
        .string "Max int: %d\n"
.LC1:
        .string "Max long: %lu\n"
main:
        push    rbp
        mov     rbp, rsp
        mov     esi, 2147483647
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        movabs  rsi, 9223372036854775807
        mov     edi, OFFSET FLAT:.LC1
        mov     eax, 0
        call    printf
        mov     eax, 0
        pop     rbp
        ret
        mov     esi, 2147483647
...
        movabs  rsi, 9223372036854775807

The first number is the typical maximum int for GCC, and the second number is the typical maximum long for GCC.


Though, looking at the first screenshot, clicking the box to target x86_64 might fix this. That adds the -m64 flag and apparently the default in this particular install is to target x86 -m32? But the question is which 64 bit architecture model it targets.


Re operating systems, most applications on Windows use LLP64, but applications and compilers are not obligated to do so. This is a compiler decision. LP64 makes more sense and is used by other operating systems, but Windows does what Windows wants. I think you should be able to bypass this silliness on Microsoft’s part by using WSL/WSL2. (LLP64 vs LP64 explained)


Well, thanks for making it to the end. TLDR, check the C standard, your values in limits.h, your compiler flags, and the emitted assembly code via GodBolt. This was a cool thing to dig into.

2 Likes