【问题标题】:Standard (cross-platform) way for bit manipulation位操作的标准(跨平台)方式
【发布时间】:2012-09-25 16:04:05
【问题描述】:

由于数字的不同二进制表示(例如,采用大/小端),这是跨平台的吗:

// NOTE: FIXED-SIZE unsigned integral type
some_unsigned_type variable = some_number;

// set n-th bit, starting from 1,
// right-to-left (least significant-to most significant)
variable |= ( 1 << ( n - 1 ) );

// clear the same bit:    
variable &= ~( 1 << ( n - 1 ) );

换句话说,编译器是否总是处理固定大小无符号数的不同二进制表示,还是特定于平台的?

如果variable是有符号整数类型(例如int)并且它的值为

  • 零
  • 阳性
  • 否定的?

标准对此有何规定?

附注而且,是的,我对 C 和 C++ 都很感兴趣,请不要告诉我它们是不同的语言,因为我知道 :)

如果需要,我可以粘贴真实示例,但帖子会变得太长

【问题讨论】:

  • C 的哪个版本?如果您的目标是 C99,则可以访问 stdint.h,它为您提供了许多整数类型的跨平台选项。
  • 即使您对 C++ 和 C 这两种语言都感兴趣,但并非所有人都精通这两种语言,以确保提供真正适用于这两种语言的答案。

标签: c++ c cross-platform standards bit-manipulation


【解决方案1】:

免责声明:我隐含地假设您在谈论具有固定宽度的整数类型。否则位移是非常危险的......

标准:n3337 C++11

对于无符号类型或有符号类型 (*) 中的正值,移位的定义是数学的,因此不受底层硬件表示的影响。

5.8 移位运算符 [expr.shift]

2E1 &lt;&lt; E2的值是E1左移E2位位置;空出的位用零填充。如果E1 具有无符号类型,则结果的值为E1 × 2E2,以比结果类型中可表示的最大值多1 为模减少。否则,如果E1 具有带符号类型和非负值,并且E1×2E2 在结果类型中是可表示的,那么这就是结果值;否则,行为未定义。

3E1 &gt;&gt; E2 的值是E1 右移E2 位位置。如果E1 具有无符号类型或E1 具有有符号类型且非负值,则结果的值是E1/2 商的整数部分E2 .如果E1 具有带符号类型和负值,则结果值是实现定义的。

出于同样的原因,我认为按位计算 and、or 和 negate 是可以的:它们是用数学方法定义的。

5.3.1 一元运算符 [expr.unary.op]

10 ˜ 的操作数应为整数或非范围枚举类型;结果是其操作数的反码。

5.11 位与运算符 [expr.bit.and]

1 执行通常的算术转换;结果是操作数的按位与函数。该运算符仅适用于整数或无范围枚举操作数。

5.13 按位包含 OR 运算符 [expr.or]

1 执行通常的算术转换;结果是其操作数的按位包含 OR 函数。该运算符仅适用于整数或无范围枚举操作数。

但是我承认我对后两者不太确定,我找不到 按位 XX 函数的任何定义,所以即使我相信它们指的是数学对应物,我也不能保证.

(*) 感谢 phresnel 指出这一点。

【讨论】:

  • [...] definition of shifts is [...] not affected by [...] hardware representation. 对比 If E1 has a signed type and a negative value, the resulting value is implementation-defined.
  • 是的,我说的是固定大小的整数类型。我不好不提。关于* - “无符号类型或有符号类型中的正值” - 零呢?正向不等于非负向,对吧?
  • @KirilKirov:其实 0 应该没问题。例如,对于移位,有符号整数的最左边存在一个问题:在 2 补码中,它表示符号(0 正,1 负),因此对于负值,通过移位实际上会改变符号......哎呀!
  • 是的,你说得对,你说的对课程。你能告诉我一些关于报价的事情吗?它们到底是从哪个标准来的?您知道它们是否适用于所有标准?
  • @KirilKirov:我在答案中编辑了标准的版本,你说得对,它非常重要。我的理解是0通常被认为是积极的,所以我觉得没有必要精确。
【解决方案2】:

如果您移动负数(或有符号)数字(通过大多数实现是相同的 U2 事物),则它是特定于实现的。如果您将无符号数字移动的值不超过变量中的位数,它对于大多数用途都是可移植的。

【讨论】:

    【解决方案3】:

    除非some_unsigned_type 是固定宽度类型,否则这是您的第一个特定平台。在一个平台上,您可能会转移一些价值本身永远不会重现的信息,而在另一个平台上可能不会。示例:

    16 bit 'int':
    
          1000 0000  0000 0000
    <<1 = 0000 0000  0000 0000
    >>1 = 0000 0000  0000 0000
    
    32 bit 'int':
    
          0000 0000  0000 0000   1000 0000  0000 0000
    <<1 = 0000 0000  0000 0001   0000 0000  0000 0000
    >>1 = 0000 0000  0000 0000   1000 0000  0000 0000
    

    C++ 标准中的5.8 Shift Operators 也这样说:

    如果正确的操作数,则行为未定义 为负数,或大于或等于提升的左操作数的位长度。

    因此,如果您将整数移位的位数比实际位数多,您将进入未定义的行为。例如,如果您将 short 值左移 17 位,它可能会在某些机器上为您提供 UB,但不是全部。

    C11 在6.5.7 Bitwise shift operators 中说,除此之外,这个:

    E1 &gt;&gt; E2 的结果是E1 右移了E2 位位置。如果E1 具有无符号类型 或者如果E1 具有带符号类型和非负值,则结果的值是整数 E1 / 2E2 商的一部分。 如果E1 具有带符号类型和负值,则 结果值是实现定义的。

    所以有符号数移位是不可移植的。

    所以,general 中整数的general 答案是:

    整数的按位操作不可移植。

    【讨论】:

    • 还有第 3.9.1/7 节“本国际标准允许整数类型的 2 的补码、1 的补码和带符号的幅度表示”。忘记换档。 有符号整数不可移植。此外,字节序。 任何整数都不可移植.
    • @MooingDuck:很好,虽然这需要来自标准的一些额外支持,即“使用按位运算,您可以操纵物理位”。当然,一种语言可以通过将是翻译成你看到的内容来实现位操作的可移植性。我的目标是通过实际的东西来消除这个问题:)
    • 没错,字节序与位运算无关。但是 1s 恭维 vs 2s 恭维,这不会混淆&amp; 的情况吗?如果我取-1,并将 MSB 位设为 0,1s 恭维和 2s 恭维会给出不同的结果,不是吗?
    • @Mooing Duck:我的意思是,即使机器在底层使用了另一个补充,语言标准可能仍然要求它将这些位“翻译”成一致的东西;我知道这听起来很奇怪,但快速思考,并非不可能。
    • 我只是检查了规范,除了积分促销,我什么都没看到。 (不是说不存在,只是没看到)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-03
    • 1970-01-01
    • 2018-05-09
    • 2012-10-17
    • 1970-01-01
    相关资源
    最近更新 更多