Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 228
Auden Young Avatar asked Mar 20 '26 05:03

Auden Young


1 Answers

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.

like image 68
abarnert Avatar answered Mar 22 '26 18:03

abarnert



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!