此代码确实计算了 8 位二进制数的二进制补码,在 stdint.h 定义 uint8_t 的任何实现中:
#include <stdint.h>
uint8_t twos_complement(uint8_t val)
{
return -(unsigned int)val;
}
那是因为,如果 uint8_t 可用,它必须是正好为 8 位宽的无符号类型。必须转换为unsigned int,因为uint8_t 肯定比int 窄。如果没有转换,值将在被否定之前提升为int,因此,如果您在非二进制补码机器上,则不会采用二进制补码。
更一般地说,此代码计算具有 any 无符号类型的值的二进制补码(使用 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);
}
因为一元减号被定义为在应用于无符号类型时采用二进制补码。我们仍然需要转换为不小于int 的无符号类型,但现在我们需要它至少与T 一样宽,因此是uintmax_t。
然而,一元减号 not 必须计算类型为 signed 的值的二进制补码,因为 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)));
}
即转换为相应的无符号类型,然后转换为uintmax_t,然后应用一元减号,然后反向转换为可能有符号的类型。 (需要强制转换为 U 以确保该值为零而不是从其自然宽度进行符号扩展。)
(但是,如果您发现自己这样做了,请停止并将相关类型更改为无符号。您未来的自己会感谢您。)