【问题标题】:Flipping bytes, doing arithmetic and flipping them back again翻转字节,做算术并再次翻转它们
【发布时间】:2019-04-30 00:46:01
【问题描述】:

我有一个关于在大端和小端之间转换以及做算术的编程/数学问题。

假设我们有两个处于小端模式的整数:

int a = 5;
int b = 6;
//a+b = 11

让我们翻转字节并再次添加它们:

int a = 1280;
int b = 1536;
//a+b = 2816

现在如果我们翻转2816 的字节顺序,我们会得到11。所以本质上我们可以在小端和大端之间进行算术计算,一旦转换它们代表相同的数字?

这背后有计算机科学界的理论/名称吗?

【问题讨论】:

  • 你刚好选了两个不会溢出的数字。
  • 这就像说乘法和加法是一样的,因为2+2==2*2

标签: c endianness


【解决方案1】:

如果添加涉及携带,则它不起作用,因为携带从右到左传播。交换数字并不意味着携带切换方向,因此任何溢出到下一个字节的字节都会不同。

让我们看一个十六进制的例子,假设字节顺序意味着每个 4 位半字节被交换:

int a = 0x68;
int b = 0x0B;
//a+b:  0x73

int a = 0x86;
int b = 0xB0;
//a+b: 0x136

816 + B16 是 1316。该 1 被携带并添加到第一个总和中的 6 上。但在第二个和中,它没有向右进位并添加到 6,它向左进位并溢出到第三个十六进制数字。

【讨论】:

    【解决方案2】:

    首先,应该注意的是,您认为int 在 C 中具有 16 位错误的。在大多数现代系统中,int 是 32 位类型,所以如果我们 reverse(不翻转,这通常意味着取补码)5 的字节,我们将得到 83886080 (0x05000000),而不是1280 (0x0500)

    还要注意,你应该用十六进制写,这样更容易理解,因为计算机不能用十进制工作:

    int16_t a = 0x0005;
    int16_t b = 0x0006;
    // a+b = 0x000B
    
    int16_t a = 0x0500; // 1280
    int16_t b = 0x0600; // 1536
    //a+b = 0x0B00
    

    现在就像其他人所说的那样,ntohl(htonl(5) + htonl(6)) 恰好与 5 + 6 相同只是因为你的数字很小,它们的反向总和不会溢出。选择更大的数字,您会立即看到差异


    但是,对于像这种情况下将值存储在 2 个较小部分中的系统,该属性确实适用于 ones' complement

    在一个补码中,通过将进位传播回进位来与end-around carry 进行算术运算。如果只有一个内部“进位中断”,这使得一个补码算术 endian 独立 (即存储的值被分成两个单独的块)因为“循环进位”

    假设我们有 xxyy 和 zztt 然后 xxyy + zztt 是这样完成的

                carry
            xx        yy
          + zz <───── tt
          ──────────────
      carry aa        bb
         │             ↑
         └─────────────┘
    

    当我们反转块时,yyxx + ttzz 以相同的方式进行。因为 xx、yy、zz、tt 是任意长度的位块,所以它适用于 PDP 的混合字节序,或者当您将 32 位数字存储在两个 16 位部分中时,将 64 位数字存储在两个 32 位部分中...

    例如:

    • 0x7896 + 0x6987 = 0xE21D
      • 0x9678 + 0x8769 = 0x11DE1 → 0x1DE1 + 1 = 0x1DE2
    • 0x2345 + 0x9ABC = 0xBE01
      • 0x4523 + 0xBC9A = 0x101BD → 0x01BD + 1 = 0x01BE
    • 0xABCD + 0xBCDE = 0x168AB → 0x68AB + 1 = 0x68AC
      • 0xCDAB + 0xDEBC = 0x1AC67 → 0xAC67 + 1 = 0xAC68

    或者上面 John Kugelman 的例子:0x68 + 0x0B = 0x73; 0x86 + 0xB0 = 0x136 → 0x36 + 1 = 0x37

    end-around 进位是 TCP 校验和选择反码的原因之一,因为您可以轻松地计算出更高精度的和。 16 位 CPU 可以像正常一样在 16 位单元中工作,但是 32 位和 64 位 CPU 可以并行添加 32 位和 64 位块,而无需担心 SIMD 不可用时的进位,例如SWAR technique

    【讨论】:

      【解决方案3】:

      这似乎只起作用,因为您碰巧选择了足够小的数字,以便它们以及它们的总和适合一个字节。只要您的号码中发生的所有事情都保持在其各自的字节内,您显然可以随心所欲地洗牌和解洗您的字节,这不会有什么不同。如果您选择较大的数字,例如 1234 和 4321,您会注意到它不再起作用。事实上,您很可能最终会调用未定义的行为,因为您的 int 会溢出……

      除此之外,您几乎肯定会想阅读以下内容:https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html

      【讨论】:

        猜你喜欢
        • 2011-05-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多