校验和计算 - 所有字节的二进制补码

时间:2012-09-02 18:18:04

标签: c++ algorithm checksum

我有关于创建如下所述消息的校验和的说明:

校验和由一个字节组成,该字节等于从“消息类型”字一直到消息块结尾的所有字节的二进制补码和(不包括传输的校验和)。从最重要的位置进行忽略。

我发现的另一个描述是: 校验和值包含数据消息中其他字的模256和的二进制补码(即,消息类型,消息长度和数据字)。接收设备可以计算接收字的模256和,并将该和加到接收的校验和字上。零结果通常表示消息已正确接收。

我理解这意味着我将消息中所有字节的值相加(excl checksum),得到这个数字的模256。获得这个数字的两个补码,这是我的校验和。

但是我遇到了一个示例消息示例的问题(来自设计文档,所以我必须假设它已被正确编码)。

unsigned char arr[] = {0x80,0x15,0x1,0x8,0x30,0x33,0x31,0x35,0x31,0x30,0x33,0x30,0x2,0x8,0x30,0x33,0x35,0x31,0x2d,0x33,0x32,0x31,0x30,0xe};

所以最后一个字节0xE是校验和。我计算校验和的代码如下:

bool isMsgValid(unsigned char arr[], int len) {
   int sum = 0;
   for(int i = 0; i < (len-1); ++i) {
      sum += arr[i];
   }
   //modulo 256 sum
   sum %= 256;

   char ch = sum;

   //twos complement
   unsigned char twoscompl = ~ch + 1;

   return arr[len-1] == twoscompl;
}


int main(int argc, char* argv[])
{
   unsigned char arr[] = {0x80,0x15,0x1,0x8,0x30,0x33,0x31,0x35,0x31,0x30,0x33,0x30,0x2,0x8,0x30,0x33,0x35,0x31,0x2d,0x33,0x32,0x31,0x30,0xe};
   int arrsize = sizeof(arr) / sizeof(arr[0]);

   bool ret = isMsgValid(arr, arrsize);

   return 0;
}

规范如下:= http://www.sinet.bt.com/227v3p5.pdf

我认为我误解了所需的算法。知道如何创建这个校验和吗?

Flippin spec writer在他们的数据示例中犯了一个错误。刚发现它然后回到这里,发现其他人也发现了。对不起,如果我浪费你的时间。我将研究回答,因为它看起来像是一些有用的注释,用于改进我的代码。

3 个答案:

答案 0 :(得分:5)

您错误地使用了链接的pdf中的示例消息。第二个参数长度为9个字节,但您在代码中使用了0x08。

当参数中确实有9个字节时,文档错误地在第三列中指出“8个字节”。第二列正确显示“00001001”。

换句话说,您的测试消息应为:

{0x80,0x15,0x1,0x8,0x30,0x33,0x31,0x35,0x31,0x30,0x33,0x30, // param1
 0x2,0x9,0x30,0x33,0x35,0x31,0x2d,0x33,0x32,0x31,0x30,0xe}  // param2
     ^^^

使用正确的消息数组{I},当我尝试您的程序时。

答案 1 :(得分:1)

同意评论:看起来校验和错误。 .PDF中的数据是什么?

一些一般提示:

使用无符号类型作为累加器;这会在溢出时为您提供明确定义的行为,并且您需要更长的消息。类似地,如果将结果存储在char变量中,则将其设为unsigned char。

但你不需要存储它;只需用无符号类型进行数学运算,补充结果,加1,然后屏蔽高位,以便得到8位结果。

此外,这里还有一个技巧,如果您使用的是使用二进制补码算法的硬件:只需添加所有值,包括校验和,然后屏蔽高位;如果输入正确,结果将为0。

答案 2 :(得分:1)

  

接收设备可以计算接收字的模256和,并将该和加到接收到的校验和字上。

使用这个条件来理解校验和要容易得多:

{byte 0} + {byte 1} + ... + {last byte} + {checksum} = 0    mod 256
{checksum} = -( {byte 0} + {byte 1} + ... + {last byte} )   mod 256

正如其他人所说,在使用单个位时,你真的应该使用无符号类型。在进行模运算时也是如此。如果您使用签名类型,则会让自己处于与大量相关的错误中。 OTOH,几乎是你打开使用无符号数字的唯一错误,就像遗忘2u-3u是一个正数。

(请注意将有符号和无符号数字混合在一起:这里也涉及很多细微之处)