如何在c ++中取一个字节的二进制补码

ama*_*ate 6 c++ byte twos-complement

我正在看一些c ++代码,我看到:

 byte b = someByteValue;
 // take twos complement
 byte TwosComplement = -b;
Run Code Online (Sandbox Code Playgroud)

这段代码是否采用b的两个补码?如果没有,它在做什么?

zwo*_*wol 11

在任何stdint.h定义的实现中,代码肯定会计算8位二进制数的二进制补码uint8_t:

#include <stdint.h>
uint8_t twos_complement(uint8_t val)
{
    return -(unsigned int)val;
}
Run Code Online (Sandbox Code Playgroud)

这是因为,如果 uint8_t可用,它必须是一个正好是8位宽的无符号类型.转换unsigned int是必要的,因为uint8_t它肯定比较窄int.没有转换,该值将int在被否定之前被提升,因此,如果您使用的是非二进制补码机器,则不会使用二进制补码.

更一般地,该代码计算的值与所述二进制补码的任何无符号类型(用C++结构用于说明-一元减号的行为是在两种语言相同,假定没有用户定义过载):

#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);
}
Run Code Online (Sandbox Code Playgroud)

因为一元减号被定义为在应用于无符号类型时采用二进制补码.我们仍然需要一个无符号类型的强制转换int,但现在我们需要它至少与任何可能的一样宽T,因此uintmax_t.

然而,一元减号也没有必要计算其类型的值的二进制补码,因为C(和C++)仍明确允许基于这样的CPU实现使用二进制补码有符号的数量.据我所知,至少20年内没有制造过这样的CPU,所以继续为它们提供的服务有点傻,但确实如此.如果你想计算一个值的二进制补码,即使它的类型恰好是有符号的,你必须这样做:(C++再次)

#include <type_traits>

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

    return T(-uintmax_t(U(val)));
}
Run Code Online (Sandbox Code Playgroud)

即转换为相应的无符号类型,然后转换为uintmax_t,然后应用一元减号,然后反向转换为可能签名的类型.(对U的强制转换需要确保该值为零,而不是从其自然宽度进行符号扩展.)

(如果你发现自己这样做了,请停止并将有问题的类型改为无符号.未来的自我会感谢你.)


Vla*_*cow 6

正确的表达式看起来

byte TwosComplement = ~b + 1;
Run Code Online (Sandbox Code Playgroud)

注意:前提是字节iis定义为 unsigned char