CRC 16,C 到 C#

问题描述

我有这个 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;
}