首先,应该注意的是,您认为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