将CRC8从C语言翻译成Java

3

我是一名有用的助手,可以为您翻译文本。

我收到了一段用C语言编写的代码,用于计算字节数组的CRC8值。现在需要将其翻译成Java。

以下是C代码:

CRC_POLYNOM = 0x9c;
CRC_PRESET = 0xFF;

unsigned int CRC = CRC_PRESET;
for (i = 0; i < Len; i++)
{
  crc ^= FRAME[i];
  for (j = 0; j < 8; j++)
  {
    if (crc & 0x01)
        crc = (crc >> 1) ^ CRC_POLYNOM;
    else
        crc = (crc >> 1);
  }
}

我在Java中所实现的是这样的:

public static long calculateCRC8(byte[] b, int len) {
  long crc = CRC_PRESET;
  for (int i = 0; i < len; i++) {
    crc ^= b[i];
    for (int j = 0; j < 8; j++) {
      if ((crc & 0x01) == 0)
        crc = (crc >> 1) ^ CRC_POLYNOM;
      else
        crc = crc >> 1;
    }
  }
return crc;
}

对于一个示例的字节数组:

byte[] b = new byte[] {1, 56, -23, 3, 0, 19, 0, 0, 2, 0, 3, 13, 8, -34, 7, 9, 42, 18, 26, -5, 54, 11, -94, -46, -128, 4, 48, 52, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 1, -32, -80, 0, 98, -5, 71, 0, 64, 0, 0, 0, 0, -116, 1, 104, 2};

C代码返回29,而我的Java代码返回44。我做错了什么?

我觉得这是因为Java只支持带符号的数据类型,那么该如何解决呢?

4个回答

7
if (crc & 0x01)

这个测试用于检测最低位是否被设置。

if ((crc & 0x01) == 0)

这个测试用于检查最低位是否是清除的

在Java代码中,你还应该使用无符号右移(即>>>而不是>>),并在返回结果前使用0xff进行掩码处理。

编辑:最后,您需要更改此处:

crc ^= b[i];

转换为:

crc ^= b[i] & 0xff;

然而,你应该彻底放弃这种方法,转而使用表驱动的方式。相比于此方法,后者速度快了8倍。

编辑2 表驱动版本已修订,以实现java.util.zip.Checksum

public class CRC8 implements Checksum
{
    private final short init;
    private final short[]   crcTable = new short[256];
    private short   value;

    /**
     * Construct a CRC8 specifying the polynomial and initial value.
     * @param polynomial Polynomial, typically one of the POLYNOMIAL_* constants.
     * @param init Initial value, typically either 0xff or zero.
     */
    public CRC8(int polynomial, short init)
    {
        this.value = this.init = init;
        for (int dividend = 0; dividend < 256; dividend++)
        {
            int remainder = dividend ;//<< 8;
            for (int bit = 0; bit < 8; ++bit)
                if ((remainder & 0x01) != 0)
                    remainder = (remainder >>> 1) ^ polynomial;
                else
                    remainder >>>= 1;
            crcTable[dividend] = (short)remainder;
        }
    }

    @Override
    public void update(byte[] buffer, int offset, int len)
    {
        for (int i = 0; i < len; i++)
        {
            int data = buffer[offset+i] ^ value;
            value = (short)(crcTable[data & 0xff] ^ (value << 8));
        }
    }

    /**
     * Updates the current checksum with the specified array of bytes.
     * Equivalent to calling <code>update(buffer, 0, buffer.length)</code>.
     * @param buffer the byte array to update the checksum with
     */
    public void update(byte[] buffer)
    {
        update(buffer, 0, buffer.length);
    }

    @Override
    public void update(int b)
    {
        update(new byte[]{(byte)b}, 0, 1);
    }

    @Override
    public long getValue()
    {
        return value & 0xff;
    }

    @Override
    public void reset()
    {
        value = init;
    }

    public static void  main(String[] args)
    {
        final int   CRC_POLYNOM = 0x9C;
        final byte  CRC_INITIAL = (byte)0xFF;

        final byte[]    data = {1, 56, -23, 3, 0, 19, 0, 0, 2, 0, 3, 13, 8, -34, 7, 9, 42, 18, 26, -5, 54, 11, -94, -46, -128, 4, 48, 52, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 1, -32, -80, 0, 98, -5, 71, 0, 64, 0, 0, 0, 0, -116, 1, 104, 2};
        CRC8    crc8 = new CRC8(CRC_POLYNOM, CRC_INITIAL);
        crc8.update(data,0,data.length);
        System.out.println("Test successful:\t"+(crc8.getValue() == 29));
    }
}

如果 ((crc & 0x01) == 0x01) crc = ((crc >> 1) & 0xff) ^ CRC_POLYNOM; else crc = (crc >> 1) & 0xff; } } 返回 crc; 我已经使用 0xff 掩盖了三次右移。现在它返回 75。 - Alexander Ciesielski
@AlexanderCiesielski 请继续关注,我会发布表格代码的,但可能不是今晚,因为现在是我当地时间晚上10:30。 - user207421
将 'update(int)' 替换为: public void update(int b) { update(ByteBuffer.allocate(Integer.BYTES).putInt(b).array(), 0, Integer.BYTES); } - dr_begemot
我实现了这个版本,但是没有像所有在线CRC8计算器一样得到相同的结果。我错过了什么吗? - Zofren
我需要计算一个CRC8,其多项式为0xEA,初始值为0x000,最高位优先(非反转),没有最终异或。 - Zofren

2
你的"^"实际上已经像箭头一样指向了错误所在的部分。
等同于。
if (crc & 0x01)

在Java中,由于if语句需要布尔表达式,因此应该这样写:
if ((crc & 0x01) != 0)

或者

if ((crc & 0x01) == 0x01)

谢谢回答。但现在它返回的是47而不是44。 - Alexander Ciesielski
在我的答案中提到的所有更正都已经进行了吗?在所有四个地方都进行了吗?在C代码中,'crc'是否初始化为CRC_PRESET? - user207421
是的,我猜这里应该用 >>> 而不是 >>,就像 EJP 的同时回答一样。这里的语义不同,因为 Java 没有 C 中的“无符号”类型。有时候在转换为较小的类型时也很麻烦。 - michael_s
当我切换到 >>> 并使用 0xff 进行掩码处理时,现在得到了 75。 - Alexander Ciesielski
@AlexanderCiesielski 如果您要评论我的答案,请在我的答案下面评论。 - user207421
嗯 - 好的 - 现在得到了29。我猜测可能有一项信息遗漏了:FRAME的类型是"unsigned char",对吗?所以我们应该将crc ^= FRAME[i]改为crc = (crc ^ FRAME[i]) & 0xFF - 这样无论你使用>>还是>>>都没关系...最好总是附上编译程序所需的所有代码! - michael_s

1
使用接受的答案,我无法获得与在线CRC8计算器(如http://www.sunshine2k.de/coding/javascript/crc/crc_js.html)匹配的校验和。
因此,我将https://github.com/RobTillaart/CRC/blob/master/CRC8.cpp从C语言翻译成Java,并成功地进行了各种初始值和多项式的测试。
    final byte[] data = {0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39}; // sample data
    final byte INIT_VAL = (byte) 0x00;
    final byte POLY = 0x07;

    int _crc = INIT_VAL;
    for (int j = 0; j < data.length; j++) {
        int value = data[j];
        _crc = _crc ^ value;
        for (int i = 8; i > 0; i--) {
            if ((_crc & (1 << 7)) > 0) {
                _crc <<= 1;
                _crc ^= POLY;
            } else {
                _crc <<= 1;
            }
        }
    }
    result = (_crc & 0xff);

1
我创建了一个完整/独立的C程序来回答你的问题:
#include <stdio.h>

#define CRC_POLYNOM 0x9c
#define CRC_PRESET 0xFF

int main() {
    unsigned int crc = CRC_PRESET;
    unsigned char FRAME[] = {1, 56, -23, 3, 0, 19, 0, 0, 2, 0, 3, 13, 8, -34, 7, 9, 42, 18, 26, -5, 54, 11, -94, -46, -128, 4, 48, 52, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 1, -32, -80, 0, 98, -5, 71, 0, 64, 0, 0, 0, 0, -116, 1, 104, 2};
    for(int i = 0; i < sizeof(FRAME); i++) {
        crc ^= FRAME[i];
        for(int j = 0; j < 8; j++) {
            if(crc & 0x01) {
                crc = (crc >> 1) ^ CRC_POLYNOM;
            } else {
                crc = (crc >> 1);
            }
        }
    }
    printf("%u\n", crc);
    return 0;
}

以下是在https://www.mtsystems.com上自动生成的等效Java代码:

package demo;

public class DemoTranslation {
    public final static int CRC_POLYNOM = 0x9c;

    public final static int CRC_PRESET = 0xFF;

    public static void main(String[] args) {
        int crc_U = CRC_PRESET;
        byte[] frame_U = {1, 56, -23, 3, 0, 19, 0, 0, 2, 0, 3, 13, 8, -34, 7, 9, 42, 18, 26, -5, 54, 11, -94, -46, -128, 4, 48, 52, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 1, -32, -80, 0, 98, -5, 71, 0, 64, 0, 0, 0, 0, -116, 1, 104, 2};
        for(int i = 0; i < frame_U.length; i++) {
            crc_U ^= Byte.toUnsignedInt(frame_U[i]);
            for(int j = 0; j < 8; j++) {
                if((crc_U & 0x01) != 0) {
                    crc_U = (crc_U >>> 1) ^ CRC_POLYNOM;
                } else {
                    crc_U = (crc_U >>> 1);
                }
            }
        }
        System.out.println(Integer.toUnsignedString(crc_U));
    }
}

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