基本的CRC32维基百科实现与在线看到的标准CRC32不同。

12

我有一个基本的CRC32实现,遵循维基百科上的Code Fragment:1示例。 我认为我已经做得很正确了,只是修改了使用n位寄存器来代替示例中的n + 1位剩余多项式。

我得到的结果与在线CRC32实现的结果不同。我需要在这里更改什么?

请忽略逻辑中的Console.Writeline语句。

    const UInt32 poly = 0x04C11DB7;

    public static UInt32 GenerateCRC_32(byte[] message)
    {
        byte[] augmentedMsg = new byte[message.Length + 4];
        message.CopyTo(augmentedMsg, 0);

        UInt32 remainder = Convert.ToUInt32(augmentedMsg[0]) << 24 |
                           Convert.ToUInt32(augmentedMsg[1]) << 16 |
                           Convert.ToUInt32(augmentedMsg[2]) <<  8 |
                           Convert.ToUInt32(augmentedMsg[3]);

        for (Int32 i = 4; i < augmentedMsg.Length; i++)
        {
            for (int bit = 0; bit < 8; bit++)
            {
                UInt32 nextBit = ((UInt32)augmentedMsg[i] >> (7 - bit)) & 0x01;
                if ((remainder & 0x80000000) > 0)
                {
                    Console.WriteLine("---------------DO XOR --------------------");
                    Console.WriteLine(Convert.ToString(((remainder << 1) | nextBit), 2).PadLeft(32, '0'));
                    Console.WriteLine(Convert.ToString(poly, 2).PadLeft(32, '0'));
                    Console.WriteLine("------------------------------------------");

                    remainder = ((remainder << 1) | nextBit) ^ poly;

                    Console.WriteLine(Convert.ToString(remainder, 2).PadLeft(32, '0'));
                    Console.WriteLine("------------------------------------------");
                }
                else
                {
                    remainder = (remainder << 1) | nextBit;

                    Console.WriteLine("--------------NO---------------------");
                    Console.WriteLine(Convert.ToString(remainder, 2).PadLeft(32, '0'));
                    Console.WriteLine("------------------------------------------");
                }
            }
        }

        Console.WriteLine(Convert.ToString(remainder, 2).PadLeft(32, '0'));
        Console.WriteLine(remainder.ToString("X"));

        return remainder;
    }

我并不打算优化逻辑,只是想用C#遵循维基百科的样例。

输入信息:'A' (十六进制:0x41);输出结果:0x30476DC0。根据这个网站,应该得到的输出结果是:0xD3D99E8B。

我认为我可能忽略了CRC的反转或初始化,但我不确定如何修改这个基本实现,以获得与该网站相同的结果。

运行我的程序后输出:

--------------NO---------------------
10000010000000000000000000000000
------------------------------------------
---------------DO XOR --------------------
00000100000000000000000000000000
00000100110000010001110110110111
------------------------------------------
00000000110000010001110110110111
------------------------------------------
--------------NO---------------------
00000001100000100011101101101110
------------------------------------------
--------------NO---------------------
00000011000001000111011011011100
------------------------------------------
--------------NO---------------------
00000110000010001110110110111000
------------------------------------------
--------------NO---------------------
00001100000100011101101101110000
------------------------------------------
--------------NO---------------------
00011000001000111011011011100000
------------------------------------------
--------------NO---------------------
00110000010001110110110111000000
------------------------------------------
00110000010001110110110111000000

最后一行转为十六进制: 0x30476DC0

@Mark Adler的评论后续:

我对上述内容进行了修改,以下是修改内容(代码中已添加注释):

  1. 初始化值改为0xFFFFFFFF
  2. 倒序输入消息字节
  3. 对最终值进行XOR运算,结果再次倒序

    public static UInt32 GenerateCRC_32(byte[] message) { byte[] augmentedMsg = new byte[message.Length + 8]; message.CopyTo(augmentedMsg, 4); // 修改以创建初始化空间

    UInt32 remainder = Convert.ToUInt32(augmentedMsg[0]) << 24 |
                       Convert.ToUInt32(augmentedMsg[1]) << 16 |
                       Convert.ToUInt32(augmentedMsg[2]) <<  8 |
                       Convert.ToUInt32(augmentedMsg[3]);
    
    remainder = ~remainder; // Overwrite the above and initialized the register to 0xFFFFFFFF
    
    for (Int32 i = 4; i < augmentedMsg.Length; i++)
    {
        byte reversedMessage = Reverse(augmentedMsg[i]); // Reversed the augmented message byte
        for (int bit = 0; bit < 8; bit++)
        {
            UInt32 nextBit = Convert.ToUInt32(reversedMessage >> (7 - bit)) & 0x1; // Use the reversed message byte
            if ((remainder & 0x80000000) > 0)
            {
                Console.WriteLine("---------------DO XOR --------------------");
                Console.WriteLine(Convert.ToString(((remainder << 1) | nextBit), 2).PadLeft(32, '0'));
                Console.WriteLine(Convert.ToString(poly32, 2).PadLeft(32, '0'));
                Console.WriteLine("------------------------------------------");
    
                remainder = Convert.ToUInt32((UInt32)((UInt32)(remainder << 1) | nextBit) ^ poly32);
    
                Console.WriteLine(Convert.ToString(remainder, 2).PadLeft(32, '0'));
                Console.WriteLine("------------------------------------------");
            }
            else
            {
                remainder = (UInt32)((UInt32)(remainder << 1) | nextBit);
    
                Console.WriteLine("--------------NO---------------------");
                Console.WriteLine(Convert.ToString(remainder, 2).PadLeft(32, '0'));
                Console.WriteLine("------------------------------------------");
            }
        }
    }
    
    Console.WriteLine(Convert.ToString(remainder, 2).PadLeft(32, '0') + "(" + remainder.ToString("X") + ")");
    
    remainder = (~remainder);
    
    Console.WriteLine("XOR ^ 0xFFFFFFFF : " + Convert.ToString(remainder, 2).PadLeft(32, '0') + "(" + remainder.ToString("X") + ")");
    
    remainder = Reverse(remainder);
    
    Console.WriteLine("Reversed the Abv : " + Convert.ToString(remainder, 2).PadLeft(32, '0') + "(" + remainder.ToString("X") + ")");
    return remainder;
    

  4. ---------------DO XOR --------------------
    11111111111111111111111111111111
    00000100110000010001110110110111
    ------------------------------------------
    11111011001111101110001001001000
    ------------------------------------------
    ---------------DO XOR --------------------
    11110110011111011100010010010000
    00000100110000010001110110110111
    ------------------------------------------
    11110010101111001101100100100111
    ------------------------------------------
    ---------------DO XOR --------------------
    11100101011110011011001001001110
    00000100110000010001110110110111
    ------------------------------------------
    11100001101110001010111111111001
    ------------------------------------------
    ---------------DO XOR --------------------
    11000011011100010101111111110010
    00000100110000010001110110110111
    ------------------------------------------
    11000111101100000100001001000101
    ------------------------------------------
    ---------------DO XOR --------------------
    10001111011000001000010010001010
    00000100110000010001110110110111
    ------------------------------------------
    10001011101000011001100100111101
    ------------------------------------------
    ---------------DO XOR --------------------
    00010111010000110011001001111010
    00000100110000010001110110110111
    ------------------------------------------
    00010011100000100010111111001101
    ------------------------------------------
    --------------NO---------------------
    00100111000001000101111110011011
    ------------------------------------------
    --------------NO---------------------
    01001110000010001011111100110110
    ------------------------------------------
    --------------NO---------------------
    10011100000100010111111001101100
    ------------------------------------------
    ---------------DO XOR --------------------
    00111000001000101111110011011000
    00000100110000010001110110110111
    ------------------------------------------
    00111100111000111110000101101111
    ------------------------------------------
    --------------NO---------------------
    01111001110001111100001011011110
    ------------------------------------------
    --------------NO---------------------
    11110011100011111000010110111100
    ------------------------------------------
    ---------------DO XOR --------------------
    11100111000111110000101101111000
    00000100110000010001110110110111
    ------------------------------------------
    11100011110111100001011011001111
    ------------------------------------------
    ---------------DO XOR --------------------
    11000111101111000010110110011110
    00000100110000010001110110110111
    ------------------------------------------
    11000011011111010011000000101001
    ------------------------------------------
    ---------------DO XOR --------------------
    10000110111110100110000001010010
    00000100110000010001110110110111
    ------------------------------------------
    10000010001110110111110111100101
    ------------------------------------------
    ---------------DO XOR --------------------
    00000100011101101111101111001010
    00000100110000010001110110110111
    ------------------------------------------
    00000000101101111110011001111101
    ------------------------------------------
    --------------NO---------------------
    00000001011011111100110011111010
    ------------------------------------------
    --------------NO---------------------
    00000010110111111001100111110100
    ------------------------------------------
    --------------NO---------------------
    00000101101111110011001111101000
    ------------------------------------------
    --------------NO---------------------
    00001011011111100110011111010000
    ------------------------------------------
    --------------NO---------------------
    00010110111111001100111110100000
    ------------------------------------------
    --------------NO---------------------
    00101101111110011001111101000000
    ------------------------------------------
    --------------NO---------------------
    01011011111100110011111010000000
    ------------------------------------------
    --------------NO---------------------
    10110111111001100111110100000000
    ------------------------------------------
    ---------------DO XOR --------------------
    01101111110011001111101000000000
    00000100110000010001110110110111
    ------------------------------------------
    01101011000011011110011110110111
    ------------------------------------------
    --------------NO---------------------
    11010110000110111100111101101110
    ------------------------------------------
    ---------------DO XOR --------------------
    10101100001101111001111011011100
    00000100110000010001110110110111
    ------------------------------------------
    10101000111101101000001101101011
    ------------------------------------------
    ---------------DO XOR --------------------
    01010001111011010000011011010110
    00000100110000010001110110110111
    ------------------------------------------
    01010101001011000001101101100001
    ------------------------------------------
    --------------NO---------------------
    10101010010110000011011011000010
    ------------------------------------------
    ---------------DO XOR --------------------
    01010100101100000110110110000100
    00000100110000010001110110110111
    ------------------------------------------
    01010000011100010111000000110011
    ------------------------------------------
    --------------NO---------------------
    10100000111000101110000001100110
    ------------------------------------------
    ---------------DO XOR --------------------
    01000001110001011100000011001100
    00000100110000010001110110110111
    ------------------------------------------
    01000101000001001101110101111011
    ------------------------------------------
    --------------NO---------------------
    10001010000010011011101011110110
    ------------------------------------------
    ---------------DO XOR --------------------
    00010100000100110111010111101100
    00000100110000010001110110110111
    ------------------------------------------
    00010000110100100110100001011011
    ------------------------------------------
    --------------NO---------------------
    00100001101001001101000010110110
    ------------------------------------------
    --------------NO---------------------
    01000011010010011010000101101100
    ------------------------------------------
    --------------NO---------------------
    10000110100100110100001011011000
    ------------------------------------------
    ---------------DO XOR --------------------
    00001101001001101000010110110000
    00000100110000010001110110110111
    ------------------------------------------
    00001001111001111001100000000111
    ------------------------------------------
    --------------NO---------------------
    00010011110011110011000000001110
    ------------------------------------------
    --------------NO---------------------
    00100111100111100110000000011100
    ------------------------------------------
    00100111100111100110000000011100(279E601C)
    XOR ^ 0xFFFFFFFF : 11011000011000011001111111100011(D8619FE3)
    Reversed the Abv : 11000111111110011000011000011011(C7F9861B)
    

    输出结果不符合预期。我使用下面的表查找代码实现了同样的操作,结果与上面完全相同(0xC7F9861B),但是是错误的。

    public static UInt32 GenerateCRC_32_from_Table(byte[] message)
        {
            byte[] augmentedMsg = new byte[message.Length + 4];
            message.CopyTo(augmentedMsg, 0);
    
            UInt32 remainder = 0xFFFFFFFF;
    
            foreach (byte msgByte in augmentedMsg)
            {
                byte reversedMsgByte = Reverse(msgByte);
                remainder = ((remainder << 8) | Convert.ToUInt32(reversedMsgByte)) ^ crc32_table[((remainder >> 24)) & 0xFF];
            }
    
            remainder = Reverse(~remainder);
            return remainder;
        }
    

    而如果我使用以下代码(避免了消息增强),就会得到正确的结果。

    public static UInt32 GenerateCRC_32_from_Table(byte[] message)
        {
            UInt32 remainder = 0xFFFFFFFF;
    
            foreach (byte msgByte in message)
            {
                byte reversedMsgByte = Reverse(msgByte);
                remainder = (remainder << 8) ^ crc32_table[((remainder >> 24) ^ Convert.ToUInt32(reversedMsgByte)) & 0xFF];
            }
    
            remainder = Reverse(~remainder);
            return remainder;
        }
    

    如评论中所提到的,Reverse() 和 poly32:

        const UInt32 poly32 = 0x04C11DB7;
    
        public static UInt32 Reverse(UInt32 message)
        {
            UInt32 msgReversed = 0;
            for (int i = 0; i < 32; i++)
            {
                msgReversed = ((message & 0x80000000) >> (31 - i)) | msgReversed;
                message = message << 1;
            }
            return msgReversed;
        }
    
        public static byte Reverse(byte message)
        {
            byte msgReversed = 0;
            for (int i = 0; i < 8; i++)
            {
                msgReversed = (byte)(((byte)((byte)(message) & 0x80) >> (7 - i)) | msgReversed);
                message = (byte)(message << 1);
            }
            return msgReversed;
        }
    

Reverse 是什么意思?我没有看到相关的代码。 - Mark Adler
你还没有定义 poly32 - Mark Adler
谢谢您的回复。很抱歉我现在没有代码,因为我在另一个地方,将在几天内粘贴我用于反转的代码。Reverse()只是反映二进制数据,您可以在我的输出中看到Reverse的示例用法。XOR ^ 0xFFFFFFFF : 11011000011000011001111111100011(D8619FE3) Reversed the Abv : 11000111111110011000011000011011(C7F9861B)。同时,对于未定义的'poly32'也很抱歉,我在第一个示例中将'poly'重命名为'poly32',因此'const UInt32 poly32 = 0x04C11DB7;'就是定义。谢谢! - Titus
@Mark Adler 我已经更新了问题并提供了详细信息。谢谢! - Titus
1个回答

5
您所提到的标准CRC是反射的,即位被颠倒,它的初始化为0xfffffff,最终的CRC与0xffffffff进行异或运算。
您正在生成未翻转位的CRC,初始CRC为零,并且在结尾没有进行异或运算。
通常要实现反转位的CRC,您需要保留输入字节,但反转多项式并向另一个方向移位。输入字节被馈送到CRC的底部而不是顶部。这还将通过增加消息方法减少要处理的总字节数四个。
更新问题后的代码试图“增强”消息,但却没有使用增强。相反,它使用从偏移量4开始的数据,这相当于从偏移量0开始使用原始消息。
相反,应该不要尝试增强消息,并将消息与CRC的顶部进行异或运算,而不是尝试将消息馈送到CRC的底部。
此外,反转CRC,应用反转的消息,然后再次反转CRC等效于不执行任何操作,而是将多项式反转并向另一个方向移位。由于多项式是一个常数,因此在编写代码时进行反转,如我的原始答案中所述。0x04c11db7反转为0xedb88320
因此,代码看起来像这样(使用C语言):
#include <stddef.h>
#include <stdint.h>

/* CRC-32 (Ethernet, ZIP, etc.) polynomial in reversed bit order. */
#define POLY 0xedb88320

/* Compute CRC of buf[0..len-1] with initial CRC crc.  This permits the
   computation of a CRC by feeding this routine a chunk of the input data at a
   time.  The value of crc for the first chunk should be zero. */
uint32_t crc32(uint32_t crc, const unsigned char *buf, size_t len)
{
    int k;

    crc = ~crc;
    while (len--) {
        crc ^= *buf++;
        for (k = 0; k < 8; k++)
            crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
    }
    return ~crc;
}

谢谢您的回复!我已经将数组中的"8"个空字节(在第二个示例中)添加到数组中,而不是"4"个空字节(如第一个示例中所示),byte[] augmentedMsg = new byte[message.Length + 8];所以实际上消息扩展的最后四个空字节存在并被使用(扩展添加到消息的末尾),即使它从4开始。我已经使用前面的"4"个空字节将剩余部分初始化为0xFFFFFFFF remainder = ~remainder; // Overwrite the above and Initialized the register to 0xFFFFFFFF - Titus
前四个字节从未被使用过,因此没有意义。 - Mark Adler
我想在最后一刻给您奖励,因为您帮了我这么多,先生!回复您上面的评论,是的,自愿添加前四个空字节并从未使用它没有任何意义。我只是保留了它,以使第一个示例到第二个示例的修改最小化。我的错!所以,只有前四个字节从未被使用,但最后四个字节被用作消息的增强。我相信有和没有增强都应该返回相同的结果,但我的结果不同(不正确)。对不起,让您无休止地烦恼。谢谢! - Titus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接