31

I just tried something in MSVC 2010 on my 32-bit machine here and found out that I can use __int64 in my programs - which actually work!

How is that possible?

phuclv
  • 37,963
  • 15
  • 156
  • 475
koa
  • 319
  • 1
  • 3
  • 3
  • 2
    You could even use standard int64_t! – el.pescado - нет войне Apr 22 '10 at 16:07
  • @el.pescado: `int64_t` is only standard in C99; it is not currently part of the C++ standard, but will be added in the forthcoming C++0x. – James McNellis Apr 22 '10 at 16:12
  • In practice your 32bit machine (ie >Pentium) has some native 64bit support, it also has 36bit of address space. It's just windows that chooses to limit you to 32bit – Martin Beckett Apr 22 '10 at 16:15
  • After reading your comment I dissasembled a release-mode program that multiplies 2 int64 values just to see if it uses a special instruction. It doesn't. Are you sure Pentium has 64-bit arithmetic support? – Blindy Apr 22 '10 at 16:43
  • Well x86 instruction set was designed for easy chaining of add, sub, mul. Unfortunately 64 bit div isn't trivially implemented although 64 bit div by 32 bit is. – Joshua Apr 23 '10 at 03:51
  • Yea I knew about that one, there's even functions in the win32 api for that. – Blindy Apr 23 '10 at 06:58
  • C++ is Turing complete, so it's possible by principle to simulate what a 64 bit machine does in hardware. – usr Jan 26 '16 at 19:30
  • Possible duplicate of [How does a 32 bit processor support 64 bit integers?](https://stackoverflow.com/questions/23038451/how-does-a-32-bit-processor-support-64-bit-integers) – phuclv Apr 07 '18 at 13:38
  • duplicates: [How is 64-bit math accomplished on a 32-bit machine?](https://stackoverflow.com/q/3190143/995714), [How does a 32 bit processor support 64 bit integers?](https://stackoverflow.com/q/23038451/995714), [Long long int on 32 bit machines](https://stackoverflow.com/q/3072444/995714) – phuclv Feb 02 '21 at 00:48
  • Unless you can prove that a turing machine cannot implement a 64-bit integer, then you shouldn't be surprised. – xaxxon Feb 02 '21 at 00:57

4 Answers4

46

Same way 32-bit arithmetic worked on 16-bit systems.

In this case, it uses 2 32-bit memory addresses to form a 64-bit number together. Addition/substraction is easy, you do it by parts, the only gotcha is taking the carry-over from the lower part to the higher part. For multiplication/division, it's harder (ie more instructions).

It's obviously slow, quite a bit slower than 32 bit arithmetic for multiplication, but if you need it, it's there for you. And when you upgrade to a 64-bit processor compiler, it gets automatically optimized to one instruction with the bigger word size.

The Visual Studio 2010 Professional implementation of 64 bit multiplication on a 32-bit processor, compiled in release mode, is:

_allmul PROC NEAR

A       EQU     [esp + 4]       ; stack address of a
B       EQU     [esp + 12]      ; stack address of b

        mov     eax,HIWORD(A)
        mov     ecx,HIWORD(B)
        or      ecx,eax         ;test for both hiwords zero.
        mov     ecx,LOWORD(B)
        jnz     short hard      ;both are zero, just mult ALO and BLO

        mov     eax,LOWORD(A)
        mul     ecx

        ret     16              ; callee restores the stack

hard:
        push    ebx

A2      EQU     [esp + 8]       ; stack address of a
B2      EQU     [esp + 16]      ; stack address of b

        mul     ecx             ;eax has AHI, ecx has BLO, so AHI * BLO
        mov     ebx,eax         ;save result

        mov     eax,LOWORD(A2)
        mul     dword ptr HIWORD(B2) ;ALO * BHI
        add     ebx,eax         ;ebx = ((ALO * BHI) + (AHI * BLO))

        mov     eax,LOWORD(A2)  ;ecx = BLO
        mul     ecx             ;so edx:eax = ALO*BLO
        add     edx,ebx         ;now edx has all the LO*HI stuff

        pop     ebx

        ret     16              ; callee restores the stack

As you can see, it's a LOT slower than normal multiplication.

Blindy
  • 65,249
  • 10
  • 91
  • 131
7

Why do you find it surprising? There's nothing to prevent the compiler from supporting 64-, 128- or more-bit integer types on a 32-bit machine. The compiler can even support 57- and 91-bit types, if it feels like it. In practice supporting 2N-bit integer arithmetic on an N-bit machine is a relatively easy task, since the instruction set of a typical machine is often designed with this kind of functionality in mind.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
4

32 bits are merely the native size of a machine word, meaning they can be processed in one go, it does not mean that larger items can't be processed at all, they just need to be processed as separate 32-bit units in multiple steps, in the same way they can be smaller than a machine word, in which case merely a portion of the full machine word will be processed.

2

It works because 64-bit integer data types are part of the language specification.

A compiler for the language MUST let you work with 64-bit integers (and get correct results, of course).

Your program must work (and work exactly the same), whether you target a 64-bit, 32-bit, 16-bit, or 8-bit machine (whatever the compiler allows).

Whoever wrote the compiler was obliged to make it do whatever is needed to make every supported data type work on every targeted processor type.

Supporting potentially "higher" data types has all been taken care of, so that you don't have to do it yourself.

How?

Obviously, accepting code that commands 16-bit arithmetic operations and translating it into machine code that runs on a 16-bit (or higher) processor is "easy" work for a compiler, almost a direct translation. Z = X + Y might translate to mov a,(X); add a,(Y); mov (Z),a;.

Conversely, accepting code that commands 64-bit arithmetic operations and translating it into machine code that runs on a 32-bit (or lower) processor is more complex. The compiler has more work to do, operating on 32-bit pieces of each operand at a time. There are more ways to do get it done.

The resulting machine code could use multiple instructions inline (bigger code, faster execution). Z = X + Y might translate to mov a,(X); adc a,(Y); mov (Z),a; mov a,CARRY; adc a,(X+1); adc a,(Y+1); mov (Z+1),a;.

The resulting machine code could call extended arithmetic subroutines (smaller code, slower execution). Z = X + Y might translate to mov a,X; call GET64; mov a,Y; call ADD64; mov a,Z; call STORE64;.

A876
  • 471
  • 5
  • 8
  • Your main point is incorrect. The C++ standard does not mandate that implementations support 64-bit integer types. – Sneftel Feb 02 '21 at 01:05
  • Thanks so much. Maybe it's de-facto. Types are specified *since C99*: [signed] long long [int]: 64 bits. Range: [−9,223,372,036,854,775,807, +9,223,372,036,854,775,807] (−9,223,372,036,854,775,808 optional). unsigned long long [int]: 64 bits. Range: [0, +18,446,744,073,709,551,615]. https://en.wikipedia.org/wiki/C_data_types. – A876 Feb 03 '21 at 20:57