Two's complement
Two's complement is an important concept in representing negative numbers. To turn a positive integer negative, flip the bits and add one.
You are encouraged to solve this task according to the task description, using any language you may know.
- Task
Show how to calculate the two's complement of an integer. (It doesn't necessarily need to be a 32 bit integer.)
6502 AssemblyEdit
8-BitEdit
LDA #%01010101
EOR #255
CLC
ADC #1 ;result: #%10101011
16-bitEdit
myVar equ $20
LDA #3
STA myVar
LDA #0
STA myVar+1 ;equivalent C: uint16_t myVar = 3;
negate:
LDA myVar+1
EOR #255
STA myVar+1
LDA myVar
EOR #255
STA myVar
CLC
ADC #1
STA myVar
;this handles the case if we started with something where the low byte was zero.
LDA myVar+1
ADC #0
STA myVar+1
68000 AssemblyEdit
MOVE.L #3,D0
NEG.L D0 ;D0 = #$FFFFFFFD
8086 AssemblyEdit
mov al,17
neg al ;8-bit
mov bx,4C00h
neg bx ;16-bit
ALGOL 68Edit
Algol 68 uses whatever representation the hardware the program is running on uses, which is almost certainly two's complement. So, as in C and most other languages, -a
two's complements a
. Using Algol 68's bit manipulation facilities, we can explicitely two's complement a positive integer, as shown in this example.
Note: BIN a converts a to a BITS (bit-string) value, the NOT operator will flip the bits and the ABS operator will convert back to an integer, so 1 + ABS NOT BIN a
is a long-winded alternative to -a
. Note in Algol 68, the BIN operator cannot be applied to negative integers, so 1 + ABS NOT BIN -3
won't work.
BEGIN
INT a := 3;
print( ( -a, " ", 1 + ABS NOT BIN a, newline ) )
END
- Output:
-3 -3
ALGOL WEdit
begin
integer a;
a := 3;
write( i_w := 1, s_w := 1, -a, 1 + number( not bitstring( a ) ) )
end.
- Output:
-3 -3
ARM AssemblyEdit
MOV R0,#0x0000000F
MOV R1,#1
MVN R0,R0 ;flips the bits of R0, R0 = 0xFFFFFFF0
ADD R0,R0,R1 ;R0 = 0xFFFFFFF1
CEdit
int a = 3;
a = -a;
ForthEdit
Forth uses two's complement internally. One can use the 'base' variable to switch from one base to another.
: 2s'complement base @ swap dup negate swap 2 base ! u. u. base ! ;
25 2s'complement
- Output:
11001 1111111111111111111111111111111111111111111111111111111111100111 ok
FreeBASICEdit
In FreeBASIC as in C, if a number n is any integer type, then -n is the two's complement of n, with type preserved.
Dim As Integer d1 = 2147483648, d2 = 2147483646
Dim As Integer b(1 To ...) = {-d1, -d1+1, -2, -1, 0, 1, 2, d1-2, d1-1}
For i As Integer = 1 To Ubound(b)
Print b(i); " -> "; -b(i)
Next i
Sleep
- Output:
0000000000000011 -> 1111111111111101
inline assemblyEdit
Dim As Integer a = &b000011
Dim As Integer a2c, l
#ifdef __FB_64BIT__
l = 16
Asm
mov rax, [a]
neg rax
mov [a2c], rax
End Asm
#else
l = 8
Asm
mov eax, [a]
neg eax
mov [a2c], eax
End Asm
#endif
Print Bin(a, l); " -> "; Bin(a2c, l)
Sleep
- Output:
-2147483648 -> 2147483648 -2147483647 -> 2147483647 -2 -> 2 -1 -> 1 0 -> 0 1 -> -1 2 -> -2 2147483646 -> -2147483646 2147483647 -> -2147483647
JEdit
J uses twos complement natively:
-3
_3
We can see this by extracting bits representing the number. In this example, we limit ourselves to 8 bits:
(8#2)#:3
0 0 0 0 0 0 1 1
(8#2)#:-3
1 1 1 1 1 1 0 1
JuliaEdit
In Julia as in C, if a number n is any integer type, then -n is the two's complement of n, with type preserved. This is true even if n is unsigned.
PerlEdit
use strict;
use warnings;
for ( -2**31, -2**31+1, -2, -1, 0, 1, 2, 2**31-2, 2**31-1 ) {
printf "$_ -> %d\n", $_ == 0 ? 0 : ~$_+1
}
Output is the same as the Wren entry.
PhixEdit
inline assemblyEdit
without js integer a = 0b000011, a2c #ilASM{ [32] mov eax,[a] neg eax mov [a2c],eax [64] mov rax,[a] neg rax mov [a2c],rax } printf(1,"%032b -> %032b\n",{a,a2c})
- Output:
00000000000000000000000000000011 -> 11111111111111111111111111111101
normal hllEdit
with javascript_semantics integer a = 0b000011 printf(1,"%032b -> %032b\n",{a,-a})
Same output (naturally the rhs is twice as long under 64 bit, in both cases)
PL/MEdit
Even though the original PL/M 8080 compiler only handles unsigned integers, -A
two's complements A
.
100H: /* TWO'S COMPLEMENT *?
/* CP/M BDOS SYSTEM CALL */
BDOS: PROCEDURE( FN, ARG ); DECLARE FN BYTE, ARG ADDRESS; GOTO 5;END;
/* CONSOLE OUTPUT ROUTINES */
PR$CHAR: PROCEDURE( C ); DECLARE C BYTE; CALL BDOS( 2, C ); END;
PR$NL: PROCEDURE; CALL PR$CHAR( 0DH ); CALL PR$CHAR( 0AH ); END;
PR$HEX: PROCEDURE( B ); /* PRINTS B AS A 2 DIGIT HEX NUMBER */
DECLARE B BYTE;
DECLARE D BYTE;
IF ( D := SHR( B, 4 ) ) > 9 THEN CALL PR$CHAR( ( D - 10 ) + 'A' );
ELSE CALL PR$CHAR( D + '0' );
IF ( D := B AND 0FH ) > 9 THEN CALL PR$CHAR( ( D - 10 ) + 'A' );
ELSE CALL PR$CHAR( D + '0' );
END PR$HEX ;
DECLARE A BYTE;
A = 1;
CALL PR$HEX( A );
CALL PR$CHAR( ' ' );
A = -A;
CALL PR$HEX( A );
CALL PR$NL;
EOF
- Output:
01 FF
QuackeryEdit
Ways of calculating the two's complement of an integer in Quackery include negate
, ~ 1+
, -1 *
, and 0 swap -
.
These are demonstrated as a dialogue in the Quackery shell.
/O> 37 negate ... Stack: -37 /O> negate ... Stack: 37 /O> ~ 1+ ... Stack: -37 /O> ~ 1+ ... Stack: 37 /O> -1 * ... Stack: -37 /O> -1 * ... Stack: 37 /O> 0 swap - ... Stack: -37 /O> 0 swap - ... Stack: 37 /O>
RakuEdit
By default Rakus integers are arbitrary sized, theoretically of infinite length. You can't really take the twos complement of an infinitely long number; so, we need to specifically use fixed size integers.
There is a module available from the Raku ecosystem that provides fixed size integer support, named (appropriately enough.) FixedInt.
FixedInt supports fixed bit size integers, not only 8 bit, 16 bit, 32 bit or 64 bit, but ANY integer size. 22 bit, 35 bit, 191 bit, whatever.
Here we'll demonstrate twos complement on a 57 bit integer.
use FixedInt;
# Instantiate a new 57(!) bit fixed size integer
my \fixedint = FixedInt.new: :57bits;
fixedint = (2³⁷ / 72 - 5¹⁷); # Set it to a large value
say fixedint; # Echo the value to the console in decimal format
say fixedint.bin; # Echo the value to the console in binary format
fixedint.=C2; # Take the twos complement
say fixedint; # Echo the value to the console in decimal format
say fixedint.bin; # Echo the value to the console in binary format
- Output:
144114427045277101 0b111111111111111110100111011001111000010101110110110101101 761030578771 0b000000000000000001011000100110000111101010001001001010011
RPLEdit
RPL can handle integers whose size can be set from 1 to 64 bits. For this task, a 8-bit size is selected with STWS
.
RPL considers 'true' integers (i.e. declared by the prefix #
) to be positive, so the two's complement can not be done by the NEG
instruction.
8 STWS BIN #3d NOT 1 +
- Output:
1: #11111101b
WrenEdit
Strictly speaking, Wren doesn't have integers. Instead all numbers are 'IEEE 754' 64 bit floating point values (their underlying C type being double) and negative numbers are therefore represented using the offset binary method rather than two's complement.
This is illustrated by running the following code:
var a = 0
a = -a
System.print(a) // -0
which produces 'negative zero' rather than just 'zero'.
However, when using the bitwise operators, Wren's VM emulates the corresponding operation in C by first converting the operands to unsigned 32 bit values, performing the operation and then converting the result back to a double.
We can therefore emulate how two's complement works on signed 32 bit integers by using the bitwise complement operator ~ to flip the bits as follows:
var pow32 = 2.pow(32)
var pow31 = 2.pow(31)
var bs = [-pow31, -pow31+1, -2, -1, 0, 1, 2, pow31-2, pow31-1]
for (b in bs) {
var b2 = ~b + 1
if (b2 > pow31) b2 = b2 - pow32
System.print("%(b) -> %(b2)")
}
- Output:
-2147483648 -> 2147483648 -2147483647 -> 2147483647 -2 -> 2 -1 -> 1 0 -> 0 1 -> -1 2 -> -2 2147483646 -> -2147483646 2147483647 -> -2147483647
XPL0Edit
int I; char C;
[I:= 123;
I:= (~I) + 1;
IntOut(0, I); CrLf(0);
C:= -123;
C:= ~(C-1);
IntOut(0, C); CrLf(0);
]
- Output:
-123 123
Z80 AssemblyEdit
8-BitEdit
Zilog Z80
ld a,%00001111
neg ;returns %11110001 in a
Game Boy
ld a,%00001111
cpl ;game boy doesn't have NEG but it has CPL which flips all the bits.
inc a ;returns %11110001 in a
16 BitEdit
NEG
and CPL
only work on the accumulator A
.
The following can be written to work with BC
, DE
, HL
, IX
, or IY
.
xor a ;ld a,0
sub c
ld c,a
sbc a ;loads &FF into A if "sub c" set the carry (borrow) flag. Otherwise, a remains zero.
sub b
ld b,a