【问题标题】:how to take twos complement of a byte in c++如何在c ++中对一个字节进行二进制补码
【发布时间】:2014-09-09 21:49:52
【问题描述】:

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

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

此代码是否采用 b 的二进制补码?如果不是,它在做什么?

【问题讨论】:

  • 信息不足。此代码可能正确或不正确,完全取决于byte 和 ABI 的定义。
  • 我猜这取决于你是否使用使用二进制补码算法的 C++ 编译器。
  • 假设定义字节是无符号的。
  • 为了琐事(因为我已经接受了一个答案)并表明@Zack 抱怨/询问详细信息是多么正确:我的字节原来是一个无符号字符。

标签: c++ byte twos-complement


【解决方案1】:

代码确实计算了 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 以确保该值为零而不是从其自然宽度进行符号扩展。)

(但是,如果您发现自己这样做了,请停止并将相关类型更改为无符号。您未来的自己会感谢您。)

【讨论】:

  • 为什么需要强制转换为 uintmax_t 而不是只做 static_cast(-static_cast(val))?
  • @FlashMcQueen 如果T(因此还有U)比int 更窄,并且你没有uintmax_t 的演员表,“整数促销”将在进行否定之前将值转换为int,从而破坏了转换为无符号类型的效果。
  • 当然但是“如果目标类型是有符号的,如果源整数可以在目标类型中表示,则值不会改变。”在这种情况下 -uintmax_t(U(val)) 的值总是可以用 T 表示,不能吗?
  • @FlashMcQueen 这不是问题。问题是如果Uint 窄,则T(-U(val)) 在整数提升后变为T(-int(val)),从而对有符号数量执行否定,因此可能 取二进制补码。
【解决方案2】:

正确的表达式会看起来

byte TwosComplement = ~b + 1;

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

【讨论】:

  • 我认为您将-~ 混淆了。无论哪种方式,根据 ABI 和 byte 是什么,它仍然可能做一些意想不到的事情。
  • @Zack 据我所知,字节总是被定义为无符号字符。
  • byte 不是标准定义的基本类型也不是 typedef-name,所以我们无法知道它的定义是什么。发现一个在其标头中执行typedef int byte; 的应用程序一点也不会让我感到惊讶。
  • @Zack:c++ 标准将“字节”的大小定义为 1。因此合理的定义仅限于 charunsigned charsigned char。当然这个定义可能不合理。
  • @Cheersandhth.-Alf 我的重点是,鉴于迄今为止提供的信息,我们根本不知道“字节”的定义是否在 OP 的代码中 “合理的”。 (我们现在知道它没有签名,但我们仍然不知道它有多宽。)
【解决方案3】:

在二进制补码机器上,否定计算二进制补码,是的。

在 Unisys 上的某物,希望现在已经死去并被掩埋(但几年前仍然存在),没有签名类型。

C 和 C++ 支持有符号整数的补码、补码和符号和大小表示,并且只有使用补码才能进行补码。


byte 作为无符号类型求反加上转换为byte 会生成二进制补码位模式,无论整数表示如何,因为转换为无符号和无符号算术是模2n em> 其中 n 是值表示位数。

即用-x赋值或初始化后的结果是2n -x是x的补码。

这并不意味着否定本身必然计算二进制补码位模式。要理解这一点,请注意byte 定义为unsigned char,并且sizeof(int) > 1,byte 值在否定之前提升为int,即否定操作是使用有符号类型完成的。但是将得到的负值转换为无符号字节,根据定义创建二进制补码位模式以及模算术和转换为无符号类型的 C++ 保证。


2 的补码形式的用处由 2n - x = 1 + ((2n - 1) - x),其中最后一个括号是全一的位模式减去 x,即x 的简单按位反转。

【讨论】:

  • 由于非合作网络和小屏幕,抱歉进行了许多编辑。 :(
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-19
  • 1970-01-01
  • 2014-09-16
  • 1970-01-01
  • 2016-05-18
相关资源
最近更新 更多