How to add couple of 32 bit numbers on a 32 bit machine but without precision loss, ie in a 64 bit "pseudo register" eax:edx
. Using Intel syntax assembler.
Assuming the 32-bit numbers you're adding are in EAX and EBX, and are unsigned:
xor edx,edx ;Set edx to zero
add eax,ebx
adc edx,0 ;edx:eax = eax + ebx
This is essentially the same as zero-extending the values to 64-bit before the addition, then doing a 64-bit addition.
For signed integers this won't work (eg "0 + (-1) != 0x00000000 + 0xFFFFFFFF != 0x00000000FFFFFFFF") because you need to sign-extend instead of zero-extending. To do that:
cdq ;Set all bits in edx to the sign of eax
xchg ebx,eax ;eax = original ebx
mov ecx,edx ;ecx:ebx = original eax sign extended
cdq ;edx:eax = original ebx sign extended
add eax,ebx
adc edx,ecx ;edx:eax = eax + ebx
A "possibly slower depending on which CPU it is" (see note) alternative is to force them into the range of unsigned integers by adding 0x80000000 to them, then correct the result by subtracting 2*0x80000000 (or 0x0000000100000000) from it. Subtracting 0x0000000100000000 is the same as subtracting 1 from the high dword, which is the same as adding 0xFFFFFFFF to the high dword, so it can be:
add eax,0x80000000
add ebx,0x80000000
xor edx,edx ;Set edx to zero
add eax,ebx
adc edx,0xFFFFFFFF ;edx:eax = eax + 0x80000000 + ebx + 0x80000000 + (-0x0000000100000000)
Note: If you care about performance; this alternative gives you the opportunity to add the 0x80000000 to the values in earlier code (wherever the values come from), and can often end up faster (especially if the same 32-bit values are used multiple times, and/or if the addition can be incorporated into other calculations for free).
For "mixed types" you'd only need to promote the signed value to 64-bit. For example, if EAX is signed and EBX is unsigned:
cdq ;Set all bits in edx to the sign of eax
add eax,ebx
adc edx,0 ;edx:eax = eax + ebx
Of course for newer CPUs you'd use 64-bit code. For unsigned 32-bit values they'll already be zero extended by default, and you'd only need a single add rax,rbx
instruction. For signed numbers you may need to sign extend (if you can't/didn't sign extend them beforehand), like:
movsx rax,eax
movsx rbx,ebx
add rax,rbx
If I understand the question correctly, you have two 32bit integers that you add to possibly give a 64bit integer. You want to do this without 32bit overflow.
How about looking at what a compiler does:
$ cat add64.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main (int argc, char **argv) {
uint32_t a, b;
a = strtoll(argv[1], NULL, 10);
b = strtoll(argv[2], NULL, 10);
printf("%lu + %lu = %llu\n", a, b, (uint64_t) a + b);
return 0;
}
$ gcc -m32 -g -o add64 add64.c
$ ./add64 3000000000 4000000000
3000000000 + 4000000000 = 7000000000
$ gcc -m32 -g -fverbose-asm -masm=intel -S add64.c
$ $EDITOR add64.s &
[5] 340
$
The relevant generated assembly is:
mov %ecx, DWORD PTR [%ebp-16] # D.2300, a
mov %ebx, 0 # D.2300,
mov %eax, DWORD PTR [%ebp-12] # D.2301, b
mov %edx, 0 # D.2301,
add %eax, %ecx # D.2302, D.2300
adc %edx, %ebx # D.2302, D.2300
In order to add 64 bit numbers on a 32 bit machine, you have to first move the top half of the 64bit number into register eax, then the second half into edx. When manipulating this number, you'll have to keep track how the number was placed in eax/edx.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.