【问题标题】:Remake of Fletcher checksum from 32bit to 8将 Fletcher 校验和从 32 位重制为 8 位
【发布时间】:2012-01-19 11:08:35
【问题描述】:

此转换是否与原始版本相符?

uint8_t fletcher8( uint8_t *data, uint8_t len )
{
    uint8_t sum1 = 0xff, sum2 = 0xff;

    while (len) {
            unsigned tlen = len > 360 ? 360 : len;
            len -= tlen;
            do {
                    sum1 += *data++;
                    sum2 += sum1;
                    tlen -= sizeof( uint8_t );
            } while (tlen);
            sum1 = (sum1 & 0xff) + (sum1 >> 4);
            sum2 = (sum2 & 0xff) + (sum2 >> 4);
    }
    /* Second reduction step to reduce sums to 4 bits */
    sum1 = (sum1 & 0xff) + (sum1 >> 4);
    sum2 = (sum2 & 0xff) + (sum2 >> 4);
    return sum2 << 4 | sum1;
    }

原文:

uint32_t fletcher32( uint16_t *data, size_t len )
{
    uint32_t sum1 = 0xffff, sum2 = 0xffff;

    while (len) {
            unsigned tlen = len > 360 ? 360 : len;
            len -= tlen;
            do {
                    sum1 += *data++;
                    sum2 += sum1;
                    tlen -= sizeof( uint16_t );
            } while (tlen);
            sum1 = (sum1 & 0xffff) + (sum1 >> 16);
            sum2 = (sum2 & 0xffff) + (sum2 >> 16);
    }
    /* Second reduction step to reduce sums to 16 bits */
    sum1 = (sum1 & 0xffff) + (sum1 >> 16);
    sum2 = (sum2 & 0xffff) + (sum2 >> 16);
    return sum2 << 16 | sum1;
    }

len 为 8。

data 将有一个 data[] (1 - 8) 的输入

其实我不知道如何处理这条线: unsigned tlen = len > 360 ? 360:长度;

也许 -> int8_t tlen = len > 255 ? 255: 长度;

【问题讨论】:

  • 您的代码似乎来自维基百科。您至少应该了解此来源,因为该材料受CC-BY-SA license 的约束。您必须将代码归因于其作者。

标签: c microcontroller pic crc microchip


【解决方案1】:

如何计算这个tlen

其实我不知道如何处理这条线: unsigned tlen = len > 360 ? 360:长度;

该行似乎来自this Wikipedia section 中的an old version。现在它已更改为 359,其基本原理在 talk page 中进行了解释。该数字仅适用于对 16 位实体求和,因为它是 n 满足

的最大数字

n(n+5)/2 × (216−1) 32

换句话说,这是您可以在不执行模归约的情况下添加块的最大次数,并且仍然避免溢出uint32_t。对于 4 位数据字和 8 位累加器,对应的值为 4,计算方法为

n(n+5)/2 × (24−1) 8

因此,如果您更改数据大小,则必须修改该行。您还可以更改代码以使用更大的数据类型来保持其总和,从而在减少之前对更多块求和。但在这种情况下,您可能需要在循环内进行不止一个归约步骤。

例如,如果您将uint32_t 用于sum1sum2,那么您可以在溢出危险之前将23927 个半字节相加,但在此之后,您需要对sum1 = (sum1 &amp; 0xf) + (sum1 &gt;&gt; 4) 形式进行多达7 次归约把它归结为10x1e 的范围,就像你原来的算法那样。将其写为(sum1 - 1)%0xf + 1 可能更有效。在这种情况下,您甚至可以将范围从 1 到 15 更改回 0 到 14,将总和初始化为 0 并将归约写入 sum1 %= 0xf。除非您需要与使用其他范围的实现兼容。

【讨论】:

  • 看来“对于4位数据字和一个8位累加器,对应的值……”正确的值是3(不是4).
【解决方案2】:

我认为您需要 0xF 掩码而不是 0xFF。 32位使用16位掩码,32的一半,你的8位使用8位掩码不是8的一半,4位是8的一半。

uint8_t fletcher8( uint8_t *data, uint8_t len )
{
    uint8_t sum1 = 0xf, sum2 = 0xf;

    while (len) {
        unsigned tlen = len > 360 ? 360 : len;
        len -= tlen;
        do {
                sum1 += *data++;
                sum2 += sum1;
                tlen -= sizeof( uint8_t );
        } while (tlen);
        sum1 = (sum1 & 0xf) + (sum1 >> 4);
        sum2 = (sum2 & 0xf) + (sum2 >> 4);
    }
    /* Second reduction step to reduce sums to 4 bits */
    sum1 = (sum1 & 0xf) + (sum1 >> 4);
    sum2 = (sum2 & 0xf) + (sum2 >> 4);
    return sum2 << 4 | sum1;
}

否则,您将创建不同的校验和,而不是 Fletcher。例如 sum1 正在执行我认为所谓的补码校验和。基本上它是 16 位在前和 4 位在你的情况下,校验和,其中进位位被添加回来。在互联网协议中使用使得修改数据包非常容易,而无需计算整个数据包的校验和,你可以仅针对现有校验和添加和减去更改。

额外的减少步骤是针对极端情况,如果 sum1 += *data = 0x1F 使用四位方案的结果,那么进位位的加法是 0x01 + 0x0F = 0x10,你需要加上那个进位在循环 0x01 + 0x00 = 0x01 之外也可以回位。否则,循环和之外的加零。根据您的架构,您可能会使用类似 if(sum1&0x10) sum1=0x01; 的方法更快地执行。比可能需要更多指令的狡猾添加东西。

它不仅仅是添加进位的校验和,而是将两者结合起来的最后一步。例如,如果您只使用 32 位 fletcher 作为 16 位校验和,那么您就浪费了时间,结果的低 16 位只是一个库存校验和,并添加了进位位,没什么特别的。 sum2 是有趣的数字,因为它是 sum1 校验和的累积(sum1 是数据的累积,sum2 是校验和的累积)。

【讨论】:

  • 很好的答案,谢谢。是的,我有点用这个 fletcher 作为校验和 ;)
  • 您的代码仍然使用tlen 变量的计算,即incorrect。由于 OP 明确表示对此线的担忧,您可能已经提到您没有解决这些担忧的事实。
【解决方案3】:

在原始版本中 sum1,sum2 是 32 位的。这就是为什么之后的位移。在您的情况下,您将 sum1,sum2 声明为 8 位,因此位移没有意义。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多