问题描述
我有这个 CRC-16 计算的代码示例,我想将它转换为 C#。它必须保持相同的逻辑,因为它适用于其他机器。
我看到了这个 crc16 from c to c# 但它不起作用(不同的结果),可能是因为变量类型。
原代码为:输入字符 = 'A',余数 = 0,输出 = 390
#define CRC_polyNOMIAL 0x8005 /* CRC polynomial */
short Calculate_CRC ( char ch,short remainder ){
char *ch_ptr;
int i;
// the different part
ch_ptr = (char*)&remainder;
*(ch_ptr+1) = *(ch_ptr+1) ^ ch; /* exclusive or ch with msb of remainder */
for (i=0; i<=7; i++)
if (remainder < 0)
{ /* msb of remainder = 1 */
remainder = remainder << 1;
remainder = remainder ^ CRC_polyNOMIAL;
}
else
remainder = remainder << 1;
return (remainder);
}
我的代码是:输入字符 = 'A',余数 = 0,输出 = 514
static public ushort func(byte ch,ushort remainder,int f) {
// the different part
byte[] rawCrc = new byte[2];
rawCrc[0] = (byte)(remainder & 0x00FF);
rawCrc[1] = (byte)((remainder >> 8) & 0x00FF);
rawCrc[0] ^= ch;
remainder &= (byte)(rawCrc[0]);
for (int i = 0; i <= 7; i++)
if ((remainder & (0x8000)) == 0) { /* msb of remainder = 1 */
remainder <<= 1;
remainder ^= POL;
}
else
remainder <<= 1;
return remainder;
}
结果有所不同,但正如我所见,我的代码仍然根据需要对 msb 进行异或运算。
我该如何解决?
TIA
解决方法
您的 C# 代码不等同于您的 C 代码。具体来说,这是:
rawCrc[0] ^= ch;
...与ch
的最低有效字节remainder
进行异或,而C代码中的相应操作是与最-重要字节。
此外,这条 C# 代码行:
remainder &= (byte)(rawCrc[0]);
... 似乎与 C 代码中的任何内容都没有对应关系,而且从表面上看是错误的。我想您的意思是将两个字节重组为 remainder
,但这并没有做到。我建议完全不使用 rawCrc
,而是做类似
remainder = remainder ^ ((ch & 0xff) << 8);
此外,此 C# if
语句中的测试:
if ((remainder & (0x8000)) == 0) { /* msb of remainder = 1 */
... 相对于 C 代码中的相应测试是颠倒的,与尾随注释断言的含义不一致。
,试试这个...它应该(应该)等同于 C 版本。请注意,我删除了 byte[]
数组。
const short CRC_POLYNOMIAL = unchecked((short)0x8005);
static short Calculate_CRC(byte ch,short remainder)
{
short ch_ptr = (short)(remainder & 0xFF);
ch_ptr |= (short)((remainder & 0xFF00) ^ (ch << 8));
// You could directly use ch_ptr from now on!
remainder = ch_ptr;
for (int i = 0; i <= 7; i++)
{
if (remainder < 0)
{ /* msb of remainder = 1 */
remainder = (short)(remainder << 1);
remainder = (short)(remainder ^ CRC_POLYNOMIAL);
}
else
{
remainder = (short)(remainder << 1);
}
}
return remainder;
}
请注意,我通常会删除 short
并将其更改为 ushort
。只是想到应该被视为比特的东西中有一点迹象就让我想吐。
const ushort CRC_POLYNOMIAL = (ushort)0x8005;
static ushort Calculate_CRC(byte ch,ushort remainder)
{
ushort remainder2 = (ushort)(remainder & 0xFF);
remainder2 |= (ushort)((remainder & 0xFF00) ^ (ch << 8));
for (int i = 0; i <= 7; i++)
{
if (remainder2 > short.MaxValue)
{ /* msb of remainder = 1 */
remainder2 = (ushort)(remainder2 << 1);
remainder2 = (ushort)(remainder2 ^ CRC_POLYNOMIAL);
}
else
{
remainder2 = (ushort)(remainder2 << 1);
}
}
return remainder2;
}
,
C 代码中的这一行:*(ch_ptr+1) = *(ch_ptr+1) ^ ch;
将 ch
异或到 remainder
的高字节。这是一种可怕的方法,因为这会在大端机器上失败。他们应该在 C 中做的是 remainder ^= (short)ch << 8;
。这将是可移植的,并且对读者来说更明显。这就是我在下面的 C# 代码中所做的,对 C# 进行了适当的转换(这似乎有非常奇怪的隐式提升规则):
static ushort Calculate_CRC(byte ch,ushort crc) {
crc ^= (ushort)(ch << 8);
for (int i = 0; i < 8; i++)
crc = (ushort)((crc & 0x8000) == 0 ? crc << 1 : (crc << 1) ^ 0x8005);
return crc;
}