Bit Pattern |
unsigned |
signed |
000 |
0 |
0 |
001 |
1 |
1 |
010 |
2 |
2 |
011 |
3 |
3 |
100 |
4 |
-4 |
101 |
5 |
-3 |
110 |
6 |
-2 |
111 |
7 |
-1 |
Bit: |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
unsigned char |
128 |
64 |
32 |
16 |
8 |
4 |
2 |
1 |
signed char |
-128 |
64 |
32 |
16 |
8 |
4 |
2 |
1 |
Regster |
Typical Name |
Unsigned Range | Signed Range |
Bits |
Hex Digits (4 bits) |
Bytes (8 bits) |
n/a |
Bit |
0..1 |
-1..0 |
1 |
less than 1 |
less than 1 |
al |
"char" (or
byte) |
255 |
-128 .. 127 |
8 |
2 |
1 |
ax |
"short"
(or WORD) |
65535 |
-32768 .. +32767 |
16 |
4 |
2 |
eax |
"int"
(DWORD) |
>4 billion |
-2G .. +2G |
32 |
8 |
4 |
rax |
"long" (or
"long long") |
>16 quadrillion |
-8Q .. +8Q |
64 |
16 |
8 |
int value=1; /* value to test, starts at first (lowest) bit */
for (int bit=0;bit<100;bit++) {
std::cout<<"at bit "<<bit<<" the value is "<<value<<"\n";
value=value+value; /* moves over by one bit (value=value<<1 would work too) */
}
return 0;
If I have an unsigned value in a small register, I can stick the
value into a bigger register by just sticking zeros into the high
bits.
mov cl,0xde
movzx eax,cl
ret
If I have a signed value in a small register, and I want to copy
the
*value* to a bigger register, I have to copy the sign bit upwards
into
the higher bits. The instruction "movsz" (move with sign
extend)
does this:
mov cl,0xde
movsx eax,cl
ret
signed |
unsigned |
|
<< left |
sal |
shl |
>> right |
sar |
shr |
The signed "sar":
mov eax,0x87654321Result: 0xFFFF8765 (a negative number) |
The unsigned "shr":mov eax,0x87654321 Result:
0x8765 |
mov edi,1000000000This program will hence return the right answer, or admit something failed. You do have to be careful to check after every possible arithmetic instruction, since the various flags get re-set after each "add".
add edi,edi
jo uhoh ; error-check the add
mov eax,edi
ret ; ordinary return
uhoh:
mov eax,-999 ; Special value to indicate we had an overflow
ret ; error return
unsigned int i=3000000000;The problem is, in assembly language:
if (i<-3) return 999; /* what?! */
else return 0;
unsigned int i=3000000000;This fixes the problem, but "unsigned long" suffers from the same exact malfunction at 15 quadrillion:
if ((long)i<(long)-3) return 999;
else return 0;
unsigned long i=15000000000000000000ul;Bottom line: you can't reliably compare signed and unsigned numbers. Pick one or the other, and stick with it.
if ((long)i<(long)-3) return 999;
else return 0;
int i=3;Note that we have to do two separate tests on "i": it could be negative, so we check against zero; or it could be n or bigger, so we compare against n. But if you look at the code the compiler generates, there's only one test on "i":
const int n=10;
int arr[n];
int foo(void) {
if (i>=0 && i<n) return arr[i];
else return 0;
}
mov edx, <<i>>Wait, only one comparison, and it's an *unsigned* compare? But "i" is signed!
mov eax,0x0
cmp edx,0x9
ja bad
... load up arr[i] ...
bad: ret