翻转字节,进行算术运算并再次将它们翻转

问题描述 投票:2回答:3

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

假设我们在小端模式下有两个整数:

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

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

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

现在如果我们翻转2816的字节顺序,我们得到11。所以基本上我们可以在little endian和big endian之间进行算术运算,一旦转换它们代表相同的数字?

这在计算机科学界是否有理论/名称?

c endianness
3个回答
0
投票

首先,应该注意的是,你假设C中的int有16位是错误的。在大多数现代系统中,int是一个32位类型,所以如果我们反向(不翻转,这是补码)5的字节,我们将获得83886080,而不是1280

好吧现在正如其他人所说的那样,ntohl(htonl(5) + htonl(6))恰好与5 + 6相同,只是因为你的数字很小,他们的反转总和不会溢出。选择更大的数字,你会马上看到差异


然而,对于那些将值存储在2个较小部分中的系统,该属性确实在ones' complement中保存,如本例所示

在一个补码中,通过将执行传播回到进位,可以与end-around carry进行算术运算。如果只有一个内部“进位中断”(即存储的值被分成两个单独的块),则使得补码算术端无关。因为“循环携带”

假设我们有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

端到端进位是为TCP校验和选择补码的原因之一,因为您可以轻松地以更高的精度计算总和。 16位CPU可以像普通一样以16位为单位工作,但是32位和64位CPU可以并行添加32位和64位块,而不必担心SIMD不可用时的进位,如SWAR技术


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中,它被向左移动并溢出到第三个十六进制数字。


0
投票

这似乎只是起作用,因为您碰巧选择了足够小的数字,以便它们和它们的总和适合一个字节。只要你的数字中的所有内容都保持在它的相应字节内,你可以显然随心所欲地随机播放你的字节,这不会产生任何影响。如果您选择较大的数字,例如1234和4321,您会注意到它将不再起作用。事实上,你很可能最终会调用未定义的行为,因为你的int会溢出......

除此之外,你几乎肯定想读这个:https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html

© www.soinside.com 2019 - 2024. All rights reserved.