【问题标题】:How can I disable scientific notation when working with very large numbers in Perl?在 Perl 中处理非常大的数字时,如何禁用科学记数法?
【发布时间】:2009-09-14 23:02:30
【问题描述】:

考虑以下几点:

print 3 ** 333; #Yields 7.6098802313206e+158

我的问题很简单:在处理非常大的数字时如何禁用科学记数法?基本上,我希望看到所有数字都逐字转储到stdout

这可能吗?

【问题讨论】:

    标签: perl formatting integer biginteger numeric


    【解决方案1】:

    Math::BigInt

    use Math::BigInt;
    $x = Math::BigInt->new("3");
    print $x ** 333;
    

    输出:

    760988023132059809720425867265032780727896356372077865117010037035791631439306199613044145649378522557935351570949952010001833769302566531786879537190794573523
    

    【讨论】:

      【解决方案2】:

      如果您想对程序中的所有整数执行此操作,只需添加:

      use bigint;
      

      如果你只想对一些整数做,你可以创建Math::BigInt对象。

      如果您使用浮点数,还有bignumMath::BigNum

      【讨论】:

        【解决方案3】:

        对于非常小的值,请参见以下代码:

        my $value = 1e-07;                 # = 0.0000001
        
        # NOPE
        
        print $value;                      # prints 1e-07, $value is a number
        print sprintf("%f", $value);       # prints 0, $value is a number
        print sprintf("%.10f", $value);    # prints 0.0000001000, $value is a number
        $value = sprintf("%.10f", $value);
        print $value                       # prints 1e-07, $value is a number
        
        # /NOPE
        
        use bignum;
        $value = ($value+0)->bstr();
        print $value;                      # prints 0.0000001, $value is a string
        no bignum;
        print $value;                      # prints 0.0000001, $value is a string
        
        # HOORAY
        

        【讨论】:

          【解决方案4】:

          对于这么大的数字,您可能拥有比用于存储数字的精度更多的数字。 (看到一个简单的可运行示例就可以解决这个问题)。

          如果您真的需要查看所有 150 位以上的数字,您应该使用 bigint(用于整数)、bigrat(用于有理数)和 bignum(用于浮点数)模块。

          【讨论】:

            【解决方案5】:

            这段代码也有同样的问题:

            #!/usr/bin/perl
            use strict;
            use warnings;
            print "Base Exp    MAX Signed-Negitive     MAX Signed-Positive            MAX Unsigned\n";
            for( my $x = 1; $x <= 64; $x++ ) {
                my $y = (2 ** $x);
                printf( "2 ^ %4d = %20d to %-20d or %20d\n",
                             $x,   $y/-2,  $y/2,    $y );
            }
            

            打印的最后两行:

            2 ^   63 = -4611686018427387904 to 4611686018427387904  or -9223372036854775808
            2 ^   64 = -9223372036854775808 to -9223372036854775808 or                   -1
            

            显然不正确,并且没有意识到 %d 转换导致了问题,我尝试了此处标记的解决方案:

            #!/usr/bin/perl
            use strict;
            use warnings;
            use Math::BigInt;
            print "Base Exp    MAX Signed-Negitive     MAX Signed-Positive            MAX Unsigned\n";
            for( my $x = Math::BigInt->new('1'); $x <= 64; $x++ ) {
                my $y = Math::BigInt->new(2 ** $x);
                printf( "2 ^ %4d = %20d to %-20d or %20d\n",
                             $x,   $y/-2,  $y/2,    $y );
            }
            

            那时我意识到 printf 'd' 转换导致了问题。阅读 Math::BigInt 似乎表明这些数字在内部存储为字符串,因此更改为 's' 转换,解决了问题:

            #!/usr/bin/perl
            use strict;
            use warnings;
            use Math::BigInt;
            print "Base Exp    MAX Signed-Negitive     MAX Signed-Positive            MAX Unsigned\n";
            for( my $x = Math::BigInt->new('1'); $x <= 64; $x++ ) {
                my $y = Math::BigInt->new(2 ** $x);
                printf( "2 ^ %4s = %20s to %-20s or %20s\n",
                             $x,   $y/-2,  $y/2,    $y );
            }
            

            现在最后两行打印正确:

            2 ^   63 = -4611686018427387904 to 4611686018427387904  or  9223372036854775808
            2 ^   64 = -9223372036854775808 to 9223372036854775808  or 18446744073709551616
            

            但是关于 Karel 的回答,恕我直言,这几乎是正确的,这也可以在不使用 BigInt (bigint, BigNum, ...) 的情况下通过使用“f”转换但精度设置为“0”来完成' 来消除这些小数:

            #!/usr/bin/perl
            use strict;
            use warnings;
            print "Base Exp    MAX Signed-Negitive     MAX Signed-Positive            MAX Unsigned\n";
            for( my $x = 1; $x <= 64; $x++ ) {
                my $y = (2 ** $x);
                printf( "2 ^ %4d = %20.0f to %-20.0f or %20.0f\n",
                             $x,   $y/-2,  $y/2,    $y );
            }
            

            这也适用于 OP 的问题:

            perl -e 'printf "%.0f\n", 3 ** 333'
            760988023132059813486251563646478824265752535077884574263917414498578085812167738721447369281049109603746001743233145041176969930222526036520619613114171654144
            

            【讨论】:

            • 使用printf "%.0f\n" 打印大整数是一个真的坏主意。请注意,您在上次打印输出中获得的输出仅在前 16 位数字中与已接受答案中的输出一致。如果要丢掉这么多的精度,还不如使用浮点数。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-07-09
            • 2021-01-27
            • 1970-01-01
            • 2021-05-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多