Я только что попробовал что-то в MSVC 2010 на моей 32-битной машине и выяснил, что могу использовать __int64 в своих программах, которые действительно работают!
- Как это возможно?
Я только что попробовал что-то в MSVC 2010 на моей 32-битной машине и выяснил, что могу использовать __int64 в своих программах, которые действительно работают!
То же самое 32-разрядная арифметика работала на 16-разрядных системах.
В этом случае он использует 2 32-разрядных адреса памяти для формирования 64-битного числа вместе. Добавление/подстановка легко, вы делаете это по частям, только полученный получает перенос от нижней части к более высокой части. Для умножения/деления это сложнее (т.е. больше инструкций).
Он явно медленный, довольно медленный, чем 32-разрядная арифметика для умножения, но если вам это нужно, это там для вас. И когда вы переходите на 64-разрядный компилятор processor, он автоматически оптимизируется для одной инструкции с большим размером слова.
В Visual Studio 2010 Professional реализация 64-разрядного умножения на 32-разрядном процессоре, скомпилированном в режиме выпуска, составляет:
_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
Как вы можете видеть, это LOT медленнее обычного умножения.
Почему вы считаете это неожиданным? Там ничего не мешает компилятору поддерживать 64-, 128- или более-битные целые типы на 32-битной машине. Компилятор может даже поддерживать 57- и 91-битные типы, если это так. На практике поддержка 2-разрядной целочисленной арифметики на N-разрядной машине является относительно простой задачей, поскольку набор команд типичной машины часто разработан с учетом такого рода функций.
32 бита - это всего лишь собственный размер машинного слова, то есть они могут обрабатываться за один раз, это не означает, что большие элементы не могут обрабатываться вообще, их просто нужно обрабатывать как отдельные 32-битные единиц в несколько шагов, так же, как они могут быть меньше машинного слова, и в этом случае будет обрабатываться только часть полного машинного слова.
Это работает, потому что 64-битные целочисленные типы данных часть спецификации языка.
Компилятор для языка ДОЛЖЕН позволить вам работать с 64-битными целыми числами (и, конечно же, получить правильные результаты).
Ваша программа должна работать (и работать точно так же), ориентируетесь ли вы на 64-разрядную, 32-разрядную, 16-разрядную или 8-разрядную машину (независимо от того, что разрешает компилятор).
Тот, кто написал компилятор, должен был сделать все необходимое, чтобы каждый поддерживаемый тип данных работал на каждом целевом типе процессора.
Поддержка потенциально "более высоких" типов данных позаботилась о, так что вам не нужно делать это самостоятельно.
Как?
Очевидно, что прием кода, который командует 16-разрядными арифметическими операциями и переводит его в машинный код, который работает на 16-разрядном (или выше) процессоре, является "легкой" работой для компилятора, почти прямого перевода. Z = X + Y
может перевести на mov a,(X); add a,(Y); mov (Z),a;
.
И наоборот, принятие кода, который командует 64-разрядными арифметическими операциями и переводит его в машинный код, который работает на 32-разрядном (или более низком) процессоре, является более сложным. У компилятора больше работы, работая на 32-битных фрагментах каждого операнда за раз. Есть еще несколько способов сделать это.
В результате машинный код может использовать несколько инструкций inline (более крупный код, более быстрое выполнение). Z = X + Y
может перевести на mov a,(X); adc a,(Y); mov (Z),a; mov a,CARRY; adc a,(X+1); adc a,(Y+1); mov (Z+1),a;
.
Полученный машинный код может вызывать расширенные арифметические подпрограммы (меньший код, более медленное выполнение). Z = X + Y
может перевести на mov a,X; call GET64; mov a,Y; call ADD64; mov a,Z; call STORE64;
.