Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to take twos complement of a byte in c++

I am looking at some c++ code and I see:

 byte b = someByteValue;
 // take twos complement
 byte TwosComplement = -b;

Is this code taking the twos complement of b? If not, What is it doing?

like image 448
amalgamate Avatar asked Sep 09 '14 21:09

amalgamate


2 Answers

This code definitely does compute the twos-complement of an 8-bit binary number, on any implementation where stdint.h defines uint8_t:

#include <stdint.h>
uint8_t twos_complement(uint8_t val)
{
    return -(unsigned int)val;
}

That is because, if uint8_t is available, it must be an unsigned type that is exactly 8 bits wide. The conversion to unsigned int is necessary because uint8_t is definitely narrower than int. Without the conversion, the value will be promoted to int before it is negated, so, if you're on a non-twos-complement machine, it will not take the twos-complement.

More generally, this code computes the twos-complement of a value with any unsigned type (using C++ constructs for illustration - the behavior of unary minus is the same in both languages, assuming no user-defined overloads):

#include <cstdint>
#include <type_traits>

template <typename T>
T twos_complement(T val,
                  // "allow this template to be instantiated only for unsigned types"
                  typename std::enable_if<std::is_unsigned<T>::value>::type* = 0)
{
    return -std::uintmax_t(val);
}

because unary minus is defined to take the twos-complement when applied to unsigned types. We still need a cast to an unsigned type that is no narrower than int, but now we need it to be at least as wide as any possible T, hence uintmax_t.

However, unary minus does not necessarily compute the twos-complement of a value whose type is signed, because C (and C++) still explicitly allow implementations based on CPUs that don't use twos-complement for signed quantities. As far as I know, no such CPU has been manufactured in at least 20 years, so the continued provision for them is kind of silly, but there it is. If you want to compute the twos-complement of a value even if its type happens to be signed, you have to do this: (C++ again)

#include <type_traits>

template <typename T>
T twos_complement(T val)
{
    typedef std::make_unsigned<T>::type U;

    return T(-uintmax_t(U(val)));
}

i.e. convert to the corresponding unsigned type, then to uintmax_t, then apply unary minus, then back-convert to the possibly-signed type. (The cast to U is required to make sure the value is zero- rather than sign-extended from its natural width.)

(If you find yourself doing this, though, stop and change the types in question to unsigned instead. Your future self will thank you.)

like image 145
zwol Avatar answered Oct 07 '22 03:10

zwol


The correct expression will look

byte TwosComplement = ~b + 1;

Note: provided that byte iis defined as unsigned char

like image 23
Vlad from Moscow Avatar answered Oct 07 '22 04:10

Vlad from Moscow