auden
auden

Reputation: 1157

Two's complement function outputs wrong result for -1

I am generating the input for an FPGA program to use the trapezoidal integration method. Basically, the functions of interest here are the invert() and twos_comp() functions; the rest is just testing (creating a square wave signal, and then iterating through and converting it into two's complement).

signals = []
bit_signals = []

def invert(bit_val):
    new_val = []
    for i in bit_val:
        new_val.append(str(int(not(int(i)))))
    return ''.join(new_val)

def twos_comp(val):
    if val < 0:
        bin_val = format(val, '08b')[1:]
        return format(int(invert(bin_val),2) + int('1', 2), '08b')
    else:
        bin_val = format(val, '08b')[1:]
        return bin_val

x = 0
signal = 1
while x <= 25:
    if x % 2 == 0:
        signal*=-1
    signals.append(signal)
    x+=1

print(signals)

for i in signals:
    bit_signals.append(twos_comp(i))

print(bit_signals)

The problem here is that this outputs the two's complement for 1 as 01111111, not 1111111. The output of invert() seems to be correct, the output for twos_comp() for positive numbers seems to be correct, and the generation of the signal also appears to be correct, so I think it must be something with the line

return format(int(invert(bin_val),2) + int('1', 2), '08b')

but looking around on SO and google this is how other people have handled adding in binary.

Please note that all inputs to twos_comp() will be 8 bits. Any help would be appreciated as to why this is not working. There are no outright errors, just an incorrect output.

You can run the code here.

Upvotes: 3

Views: 73

Answers (1)

abarnert
abarnert

Reputation: 365895

Step through the values when val is -1:

>>> format(-1, '08b')
'-0000001'

You may have already spotted the error—08b means 8 characters wide, not 8 digits. For a negative number, the - takes up 1 of the characters, so you only get 8 digits. But in case it isn't obvious why that's a problem, let's keep going:

>>> format(val, '08b')[1:]
'0000001'
>>> invert('0000001')
'1111110'
>>> int(invert('0000001'), 2)
126
>>> int('1', 2) # BTW, why do you need this instead of just 1, exactly?
1
>>> 126 + 1
127
>>> format(127, '08b')
01111111

If you want a hacky solution (and I suspect you do, since you're already going back and forth between strings and numbers all over the place), just do this:

bin_val = format(val, '09b')[-8:]

That will work for both positive and negative numbers.

Upvotes: 3

Related Questions