为什么要将XOR与文字而不是反转(非位)使用

Why use xor with a literal instead of inversion (bitwise not)

本文关键字:非位 使用 XOR 文字 为什么      更新时间:2023-10-16

我遇到了此CRC32代码,很好奇为什么作者选择使用

crc = crc ^ ~0U;

而不是

crc = ~crc;

据我所知,它们是等效的。

我什至在Visual Studio 2010中拆除了两个版本。

未优化构建:

    crc = crc ^ ~0U;
009D13F4  mov         eax,dword ptr [crc]  
009D13F7  xor         eax,0FFFFFFFFh  
009D13FA  mov         dword ptr [crc],eax 
    crc = ~crc;
011C13F4  mov         eax,dword ptr [crc]  
011C13F7  not         eax  
011C13F9  mov         dword ptr [crc],eax  

我也无法通过思考每个指令所需的周期数来证明代码合理,因为两者都应该花1个周期来完成。实际上, xor 可能不得不从某个地方加载字面的罚款,尽管我不确定这一点。

所以我认为这可能只是描述算法的首选方法,而不是一种优化...这是正确的吗?

编辑1:

,由于我只是意识到crc变量的类型可能很重要,要提及我包括整个代码(少量查找表,太大了),因此您不必遵循链接。

uint32_t crc32(uint32_t crc, const void *buf, size_t size)
{
    const uint8_t *p;
    p = buf;
    crc = crc ^ ~0U;
    while (size--)
    {
        crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
    }
    return crc ^ ~0U;
}

编辑2:

有人提出了一个优化的构建的事实,所以我已经制作了一个,并将其包括在下面。

优化构建:

请注意,整个函数(包括下面的最后一个编辑中)已列为。

// crc = crc ^ ~0U;
    zeroCrc = 0;
    zeroCrc = crc32(zeroCrc, zeroBufferSmall, sizeof(zeroBufferSmall));
00971148  mov         ecx,14h  
0097114D  lea         edx,[ebp-40h]  
00971150  or          eax,0FFFFFFFFh  
00971153  movzx       esi,byte ptr [edx]  
00971156  xor         esi,eax  
00971158  and         esi,0FFh  
0097115E  shr         eax,8  
00971161  xor         eax,dword ptr ___defaultmatherr+4 (973018h)[esi*4]  
00971168  add         edx,ebx  
0097116A  sub         ecx,ebx  
0097116C  jne         main+153h (971153h)  
0097116E  not         eax  
00971170  mov         ebx,eax  
// crc = ~crc;
    zeroCrc = 0;
    zeroCrc = crc32(zeroCrc, zeroBufferSmall, sizeof(zeroBufferSmall));
01251148  mov         ecx,14h  
0125114D  lea         edx,[ebp-40h]  
01251150  or          eax,0FFFFFFFFh  
01251153  movzx       esi,byte ptr [edx]  
01251156  xor         esi,eax  
01251158  and         esi,0FFh  
0125115E  shr         eax,8  
01251161  xor         eax,dword ptr ___defaultmatherr+4 (1253018h)[esi*4]  
01251168  add         edx,ebx  
0125116A  sub         ecx,ebx  
0125116C  jne         main+153h (1251153h)  
0125116E  not         eax  
01251170  mov         ebx,eax  

尚未提到的东西;如果此代码在具有16位unsigned int的计算机上编译,则这两个代码段为不同的

crc被指定为32位的无符号积分类型。~crc将倒转所有位,但是如果unsigned int为16位,则crc = crc ^ ~0U只会倒转16位。

我对CRC算法不太了解这是故意还是错误,也许Hivert可以澄清。尽管查看OP发布的示例代码,但确实确实会改变随后的循环。

nb。很抱歉将其发布为"答案",因为它不是答案,但是它太大了,无法适应评论:)

简短的答案是:因为它允许所有CRC的

具有统一算法

原因是:CRC有很多变体。每个都取决于用于欧几里得分裂的Z/Z2多项式。通常是使用Aram Perez在本文中描述的算法实现的。现在,根据您使用的多项式,在算法末尾有一个最终的XOR,该XOR取决于多项式的目标是消除某些角度的情况。碰巧的是,对于CRC32,这与全局相同,但对于所有CRC而言,这并非如此。

考虑一条消息,该消息以一些零位开始。直到消息中的第一个转移到消息中,其余的都不包含零以外的任何东西。这是一种危险的情况,因为以一个或多个零开头的数据包可能完全合法,并且CRC不会注意到零或添加的零。要合法!)消除这种弱点的简单方法是从剩余的剩余时间开始。称为初始剩余的参数告诉您针对特定CRC标准的值。crcslow()和crcfast()函数只需要一个小更改:

crc剩余= initial_remainder;

出于类似原因而存在最终的XOR值。要实现此功能,只需更改CRCSLOW()和CRCFAST()返回的值,如下所示:

返回(剩余 ^ final_xor_value);

如果最终的XOR值由所有XOR值组成(如CRC-32标准中所示),那么此额外的步骤将与补充最终剩余成员具有相同的效果。方式允许在您的特定应用中使用任何可能的值。

只是为了将我自己的猜测添加到混音中, x ^ 0x0001保留了最后一块,并将其flipp flipp flipp;关闭最后一位使用x & 0xFFFEx & ~0x0001;要打开最后一个位无条件使用x | 0x0001。即,如果您要做很多扭曲,您的手指可能知道这些习语,只是在不经过思考的情况下将它们推出。

我怀疑有任何深刻的原因。也许这就是作者对此的思考("我只用所有的人"),或者也许是在算法定义中表达的方式。

我认为这是出于同样的原因

const int zero = 0;

和其他写作

const int zero = 0x00000000;

不同的人认为不同的方式。即使是基本操作。