【问题标题】:How can I sum large hexadecimal values in Perl?如何在 Perl 中对较大的十六进制值求和?
【发布时间】:2010-12-19 21:49:06
【问题描述】:

我正在对一组 32 位十六进制字符串(“DEADBEEF”、“12345678”等)进行迭代,并尝试将它们相加以形成 32 位校验和。假设在下面的示例中,变量$temp 加载了一些十六进制字符串。

my $temp;
my $checksum;

for (...)
{
    #assume $temp is loaded with a new hex string here
    my $tempNum = hex ($temp);
    $checksum += $tempNum;
    $checksum &= 0xFFFFFFFF;
    print printf("checksum: %08X",$checksum);
}

前几个值是“7800798C”、“44444444”和“44444444”。输出是:

校验和:7800798C
校验和: BC44BDD0
校验和:FFFFFFFF
校验和:FFFFFFFF

等等。

你可以看到前两个总和是正确的,然后它似乎饱和了。我是否遗漏了有关 Perl 变量大小限制的内容?

EDIT:这是脚本的实际输出(字符串是十六进制字符串,值是该字符串的十进制转换,校验和是结果输出):

字符串:7800798C,值:2013297036,校验和7800798C 字符串:44444444,值:1145324612,校验和BC44BDD0 字符串:44444444,值:1145324612,校验和FFFFFFFF 字符串:44444444,值:1145324612,校验和FFFFFFFF 字符串:78007980,值:2013297024,校验和FFFFFFFF 字符串:44444444,值:1145324612,校验和FFFFFFFF

【问题讨论】:

  • 看起来很奇怪,你能不能也打印 $tempNum in that printf ?
  • 此代码不生成校验和。甚至在 Perl 中也没有。 :)
  • @bzlm:我知道这一点 :) 这只是校验和操作的一部分,其余部分似乎没有异常,所以我没有包含它。
  • 您能否发布一个实际编译和运行的简短脚本?为什么for (...)?什么是print printf(...)?

标签: perl hex checksum


【解决方案1】:

这是之前在Perl Monks 提出的问题。

答案似乎是“use integer”。

请参阅 Perl Monks 和 perldoc integer 的原始答案。

$ perl -we 'use integer; printf("%08X\n",  0xFFFF_FFFF + 0xFFFF_FFFF)'

【讨论】:

  • @Sinan:我必须回滚您的编辑:-M 不如“使用”广为人知,而且整数的 perldoc 链接很难注意到 - 它看起来像语法突出显示。跨度>
  • -M 如果你一直对人们隐藏它,它就会变得鲜为人知。 :)
【解决方案2】:

如果你的perl 是用 32 位整数编译的,导致数字大于0xffffffff 的整数运算将导致问题。例如:

my $x = hex '0x1234567890';
my $y = hex '0x1234567890';

print $x + $y,  "\n";

你会得到:

十六进制整数溢出... 十六进制数 > 0xffffffff 不可移植...

使用bignum 添加对更大整数的透明支持:

#!/usr/bin/perl

use strict; use warnings;
use bignum qw/hex/;

my $x = hex '0x7800798C';
my $chk;

for (1 .. 10) {
    $chk += $x;
    $chk &= 0xFFFFFFFF;
    printf("checksum: %08X\n", $chk);
}

验证输出是否符合您的期望:

校验和:7800798C 校验和:F000F318 校验和:68016CA4 校验和:E001E630 校验和:58025FBC 校验和:D002D948 校验和:480352D4 校验和:C003CC60 校验和:380445EC 校验和:B004BF78

没有bignum,我得到:

校验和:7800798C 校验和:F000F318 校验和:FFFFFFFF 校验和:FFFFFFFF 校验和:FFFFFFFF ...

【讨论】:

  • 你的回答对我来说似乎有点神秘。如果他继续添加数字而不是 &= 他们将得到一个浮点数。
  • 如果我最终得到一个整数,其中设置了低 32 位并且 &= 使用 0xFFFFFFFF 我肯定会得到 0xFFFFFFFF 回来,但仅限于那个加法。将任何其他数字添加到此总和将产生唯一值。换句话说,我永远不会在这个总和中得到一个无限期的 0xFFFFFFFF。
  • 但你最终不会得到一个整数。在 32 位系统上,加法会产生一个浮点数 (NV),然后当您尝试使用整数对其进行位与运算时,Perl 不知道该怎么做,并决定整数结果将为 0xFFFFFFFF。这不是一个完美的选择,但它是一个选择。 use integer 通过拒绝提升浮动来解决问题,因此整数 + 整数给出整数(必要时包装)。
【解决方案3】:

你想要什么结果? &= 将删除大于0xffffffff 的数字的任何位。不断地将数字相加只会得到一个越来越大的数字,然后是 &ed 和 0xffffffff。在某些时候,我希望你会得到除0xffffffff 以外的东西,不是吗?但它永远不会更大!

也许更好的校验和方法是将xor你的号码放在一起。

my $temp;
my $checksum;

for (...)
{
    #assume $temp is loaded with a new hex string here
    my $tempNum = hex ($temp);
    $checksum ^= $tempNum;
    print printf("checksum: %08X",$checksum);
}

这将使这些数字变得独一无二。

【讨论】:

    【解决方案4】:

    为了扩展Sinan's answer&= 运算符和%X 格式都受到编译到 Perl 中的整数大小的影响。在这种情况下,最大大小为 4294967295 或 0xFFFFFFFF。

    虽然您的变量可以保存大于此值的值,但在通过 &=sprintf("%X") 传递时,它们将被截断到此最大值。

    【讨论】:

    • 好的,这是有道理的。假设我想执行所有这些加法,然后将 32 个最低有效位打印到一个新变量中 - 我将如何实现?我了解我无法按原意使用 &= 或 sprintf。
    猜你喜欢
    • 2021-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-04
    • 1970-01-01
    • 2011-12-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多