将 C 移植到 C# 时棘手的整数溢出
Tricky integer overflow when porting C to C#
我正在从反汇编的 x86 二进制文件中移植以下 C 代码:
(unsigned __int16) ~ (_WORD) crc32 ^ length * ~crc32;
这是我在 C# 中移植的代码:
(uint) (~(ushort) crc32) ^ length * ~crc32)
下面是 table 和一些计算值。
Expected | Actual (C#)
-----------+------------
0x1082B9CB | 0x6082B9CB
0x30389AF7 | 0x20389AF7
0xD0EF1CD6 | 0xE0EF1CD6
如您所见,最后三个字节是正确的。只是第一个字节稍微移动了一点,但我看不出它与另一个字节有何不同。
关于如何修复我的 C# 代码以提供预期输出的任何想法?
示例值(C# 语法):
uint crc32 = 0x7601A9C5;
int length = 17;
它们的计算结果应为 0xD0EF1CD6
,但应给出 0xE0EF1CD6
。
编辑:
我可能忘了提一件重要的事情。输出以十六进制表示法表示为基础原始字节。
工作代码示例:https://ideone.com/Z8eONJ
Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(checksum)));
问题出在 CRC32 实现上。看起来我反汇编的二进制文件没有否定 return 值,这在大多数 CRC32 实现中通常都是这种情况。
public static class Crc32
{
private static readonly uint[] _table =
{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
...
};
public static uint ComputeChecksum(byte[] bytes)
{
var crc = 0xffffffff;
for (var i = 0; i < bytes.Length; i++)
{
var t = bytes[i];
var index = (byte) ((crc & 0xff) ^ t);
crc = (crc >> 8) ^ _table[index];
}
return ~crc; // or maybe return crc;
}
}
因此,代码:
(uint) (~(ushort) crc32) ^ length * ~crc32)
简化为:
(uint) ((ushort) crc32) ^ length * crc32)
现在它是 return 的预期输出。
我花了比我愿意承认的时间更长的时间...
我正在从反汇编的 x86 二进制文件中移植以下 C 代码:
(unsigned __int16) ~ (_WORD) crc32 ^ length * ~crc32;
这是我在 C# 中移植的代码:
(uint) (~(ushort) crc32) ^ length * ~crc32)
下面是 table 和一些计算值。
Expected | Actual (C#)
-----------+------------
0x1082B9CB | 0x6082B9CB
0x30389AF7 | 0x20389AF7
0xD0EF1CD6 | 0xE0EF1CD6
如您所见,最后三个字节是正确的。只是第一个字节稍微移动了一点,但我看不出它与另一个字节有何不同。
关于如何修复我的 C# 代码以提供预期输出的任何想法?
示例值(C# 语法):
uint crc32 = 0x7601A9C5;
int length = 17;
它们的计算结果应为 0xD0EF1CD6
,但应给出 0xE0EF1CD6
。
编辑: 我可能忘了提一件重要的事情。输出以十六进制表示法表示为基础原始字节。
工作代码示例:https://ideone.com/Z8eONJ
Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(checksum)));
问题出在 CRC32 实现上。看起来我反汇编的二进制文件没有否定 return 值,这在大多数 CRC32 实现中通常都是这种情况。
public static class Crc32
{
private static readonly uint[] _table =
{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
...
};
public static uint ComputeChecksum(byte[] bytes)
{
var crc = 0xffffffff;
for (var i = 0; i < bytes.Length; i++)
{
var t = bytes[i];
var index = (byte) ((crc & 0xff) ^ t);
crc = (crc >> 8) ^ _table[index];
}
return ~crc; // or maybe return crc;
}
}
因此,代码:
(uint) (~(ushort) crc32) ^ length * ~crc32)
简化为:
(uint) ((ushort) crc32) ^ length * crc32)
现在它是 return 的预期输出。 我花了比我愿意承认的时间更长的时间...