假设我有一个由64位Perl执行的脚本,它带有一个实际上是数字的参数,但当然首先是一个字符串(因为所有命令行参数都是字符串)。
现在,如果该参数的值适合64位无符号整数,则脚本应该对参数执行某些操作;否则,它应该以适当的错误消息中止。
检查该参数(作为字符串,即在数学运算中使用它之前)是否适合64位无符号整数的最有效方法是什么?
我已经想到的:
我可以进行字符串比较
我不想那样做,因为在那种情况下我不得不应对整理,Unicode::Collate的文档对于我的小问题看起来有点过分。
但这只是一种感觉,所以我很感激你的意见或其他意见。
旁注:我试过这个,它的工作方式与预期一致。但这只是一个快速的测试;我没有玩locales,所以在其他系统上它可能不起作用(虽然我怀疑有一个整理在“1”之前加“2”,但你永远不知道)。
比较前转换为数字不起作用:
root@spock:/root/test# perl -e '$i="18446744073709551615"+0; $j="18446744073709551616"+0; print "$i $j\n"; print(($i < $j) ? "less\n" : "greater or equal\n")'
18446744073709551615 1.84467440737096e+19
greater or equal
注意Perl如何打印第二个数字。这是最小的无符号整数,不适合64位,因此Perl将其转换为double。然后,当它以数字方式比较$i
和$j
时,它必须将$i
转换为双倍;由于此处涉及的精度损失,$i
转换为与$j
相同的值,因此比较出错。
我可以做use bigint;
。我试过这个,它表现得像预期的那样。
但这可能会导致性能急剧下降。据我所知,use bigint;
意味着使用各种重型库。
但这只是一种感觉,所以如果这是要走的路,请告诉我。
另一个想法(尚未尝试):我可以使用pack()
以某种方式从字符串化数字生成字节序列吗?然后我可以检查该字节序列的长度。如果它小于或等于8个字节,则字符串化的数字适合64位无符号整数。
你会如何解决这个问题?
答案 0 :(得分:4)
use constant MAX_UINT64 = '18446744073709551615';
my $larger_than_max =
length($s) > length(MAX_UINT64)
|| length($s) == length(MAX_UINT64) && $s gt MAX_UINT64;
假设输入匹配/^(?:0|[1-9][0-9]*)\z/
。调整为喜欢(例如处理前导零或符号)。
答案 1 :(得分:2)
您可以使用一个简单的快捷方式来消除大多数数字。十进制表示中包含19位或更少位数的任何数字都可以容纳64位整数,因此如果包含整数的字符串的长度小于20,则表示良好。
长度大于或等于21的任何字符串都不好。
UINT64_MAX
是18446744073709551615
。因此,有一些带有20位十进制数的数字可以适合64位无符号整数。有些人不能。
此时,使用ge
进行简单的字符串比较就足够了,因为无论语言环境如何,阿拉伯数字的排序都是相同的。
$ perl -E "say 'yes' if $ARGV[1] ge $ARGV[0]" 18446744073709551615 18446744073709551616
yes
答案 2 :(得分:0)
为了清楚起见,我假设输入是一串数字。
您要求最有效的方法。如果不了解输入的分布,就无法确定这一点。例如,如果输入是统一的128位整数,则最有效的方法是从以下内容开始:
if (length(@ARGV[0]) > 20) {die "Number too large.\n"}
这处理了99.9999999999%以上的案件。实际上,如果输入在256位整数中是统一的,那么您可能会因为编写简单代码而被原谅:
warn "Number too large.\n";
关于在合理的时间内反复进行一致的测试,您可以考虑使用Damian Conway的Regexp :: Number这样的正则表达式(对于带符号的64位数字,但原理是有效的)。注意,它是真实代码,它处理前导零。
'0*(?:(?:9(?:[0-1][0-9]{17}' .
'|2(?:[0-1][0-9]{16}' .
'|2(?:[0-2][0-9]{15}' .
'|3(?:[0-2][0-9]{14}' .
'|3(?:[0-6][0-9]{13}' .
'|7(?:[0-1][0-9]{12}' .
'|20(?:[0-2][0-9]{10}' .
'|3(?:[0-5][0-9]{9}' .
'|6(?:[0-7][0-9]{8}' .
'|8(?:[0-4][0-9]{7}' .
'|5(?:[0-3][0-9]{6}' .
'|4(?:[0-6][0-9]{5}' .
'|7(?:[0-6][0-9]{4}' .
'|7(?:[0-4][0-9]{3}' .
'|5(?:[0-7][0-9]{2}' .
'|80(?:[0-6])))))))))))))))))' .
'|[1-8]?[0-9]{0,18})'
与perl的启动时间(甚至是击键)相比,这应该是令人眼花fast乱的。
关于bigint,它执行速度非常快,并且包含一些很酷的优化功能,但是除非您在代码中测试了很多数字,否则上面的内容就足够了。
但是,如果您真的想燃烧橡胶,请查看perl胆量,并使用可以暴露宏SvIOK(SV *)的东西。 (有关更多详细信息,请参见https://metacpan.org/pod/release/KRISHPL/pod2texi-0.1/perlguts.pod#What-is-an-%22IV%22?)