我有一个编程/数学相关的问题关于大端和小端之间的转换和算术。
假设我们在小端模式下有两个整数:
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中的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位部分中...
例如:
或者上面的John Kugelman的例子:0x68 + 0x0B = 0x73; 0x86 + 0xB0 = 0x136→0x36 + 1 = 0x37
端到端进位是为TCP校验和选择补码的原因之一,因为您可以轻松地以更高的精度计算总和。 16位CPU可以像普通一样以16位为单位工作,但是32位和64位CPU可以并行添加32位和64位块,而不必担心SIMD不可用时的进位,如SWAR技术
如果添加涉及携带,因为从右到左进行传播,它不起作用。交换数字并不意味着携带交换机方向,因此溢出到下一个字节的任何字节都将不同。
让我们看一个十六进制的例子,假装字节序意味着交换每个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中,它被向左移动并溢出到第三个十六进制数字。
这似乎只是起作用,因为您碰巧选择了足够小的数字,以便它们和它们的总和适合一个字节。只要你的数字中的所有内容都保持在它的相应字节内,你可以显然随心所欲地随机播放你的字节,这不会产生任何影响。如果您选择较大的数字,例如1234和4321,您会注意到它将不再起作用。事实上,你很可能最终会调用未定义的行为,因为你的int
会溢出......
除此之外,你几乎肯定想读这个:https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html