如何修复这个Perl代码,以便1.1 + 2.2 == 3.3? [英] How do I fix this Perl code so that 1.1 + 2.2 == 3.3?

查看:159
本文介绍了如何修复这个Perl代码,以便1.1 + 2.2 == 3.3?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我该如何解决这个代码,使1.1 + 2.2 == 3.3?究竟是在这里造成这种行为?我隐约地熟悉四舍五入问题和浮点数学,但是我认为只应用于除法和乘法,并且在输出中可见。

code> [我@ unixbox1:〜/ perltests] GT; cat testmathsimple.pl
#!/ usr / bin / perl

use strict;
使用警告;

check_math(1,2,3);
check_math(1.1,2.2,3.3);

sub check_math {
my $ one = shift;
my $ two = shift;
my $ three = shift;

if($ one + $ two == $ three){
print$ one + $ two == $ three \\\
;
} else {
print$ one + $ two!= $ three \\\
;
}
}

[me @ unixbox1:〜/ perltests]> perl testmathsimple.pl
1 + 2 == 3
1.1 + 2.2!= 3.3



编辑:

到目前为止,大部分的答案都是这是一个浮点数问题,并提供了解决方法。我已经怀疑是这个问题了。我如何演示它?我如何让Perl输出变量的长形式?
$ b 编辑:

使用aschepler演示的sprintf技术,我现在能够看到问题。此外,按照mscha和rafl的建议,使用bignum来修正比较不相等的问题。但是,sprintf输出仍然表示数字不正确。这是对这个解决方案留下一点怀疑。



bignum是解决这个问题的好方法吗?有没有可能的副作用,我们应该看看,当把它整合到一个更大的,现有的程序?b / b> 解决方案

请参阅计算机科学家应了解的有关浮点算术的知识

这些都不是Perl特有的:有一个无数的实数,显然,它们都不能用有限的比特数来表示。 / p>

使用的具体解决方案取决于您的具体问题。你想追踪货币金额吗?如果是这样,请使用 bignum 提供的任意精度数字(使用更多的内存和更多的CPU,获得更准确的结果)。你在做数字分析吗?然后,决定要使用的精度,并使用 sprintf (如下所示)和 eq 进行比较。



您可以随时使用:

  use strict;使用警告; 

check_summation(1,$ _)代表[1,2,3],[1.1,2.2,3.3];

sub check_summation {
my $ precision = shift;
my($ x,$ y,$ expected)= @ {$ _ [0]};
my $ result = $ x + $ y;
$ b $为我的$ n($ x,$ y,$ expected,$ result){
$ n = sprintf('%。* f',$ precision,$ n);


if($ expected eq $ result){
printf%s +%s =%s\\\
,$ x,$ y,$ expected;
}
else {
printf%s +%s!=%s\\\
,$ x,$ y,$ expected;
}
return;

$ / code $ / pre
$ b $ p $输出:

 1.0 + 2.0 = 3.0 
1.1 + 2.2 = 3.3


How do I fix this code so that 1.1 + 2.2 == 3.3? What is actually happening here that's causing this behavior? I'm vaguely familiar with rounding problems and floating point math, but I thought that applied to division and multiplication only and would be visible in the output.

[me@unixbox1:~/perltests]> cat testmathsimple.pl 
#!/usr/bin/perl

use strict;
use warnings;

check_math(1, 2, 3);
check_math(1.1, 2.2, 3.3);

sub check_math {
        my $one = shift;
        my $two = shift;
        my $three = shift;

        if ($one + $two == $three) {
                print "$one + $two == $three\n";
        } else {
                print "$one + $two != $three\n";
        }
}

[me@unixbox1:~/perltests]> perl testmathsimple.pl 
1 + 2 == 3
1.1 + 2.2 != 3.3

Edit:

Most of the answers thus far are along the lines of "it's a floating point problem, duh" and are providing workarounds for it. I already suspect that to be the problem. How do I demonstrate it? How do I get Perl to output the long form of the variables? Storing the $one + $two computation in a temp variable and printing it doesn't demonstrate the problem.

Edit:

Using the sprintf technique demonstrated by aschepler, I'm now able to "see" the problem. Further, using bignum, as recommended by mscha and rafl, fixes the problem of the comparison not being equal. However, the sprintf output still indicates that the numbers aren't "correct". That's leaving a modicum of doubt about this solution.

Is bignum a good way to resolve this? Are there any possible side effects of bignum that we should look out for when integrating this into a larger, existing, program?

解决方案

See What Every Computer Scientist Should Know About Floating-Point Arithmetic.

None of this is Perl specific: There are an uncountably infinite number of real numbers and, obviously, all of them cannot be represented using only a finite number of bits.

The specific "solution" to use depends on your specific problem. Are you trying to track monetary amounts? If so, use the arbitrary precision numbers (use more memory and more CPU, get more accurate results) provided by bignum. Are you doing numeric analysis? Then, decide on the precision you want to use, and use sprintf (as shown below) and eq to compare.

You can always use:

use strict; use warnings;

check_summation(1, $_) for [1, 2, 3], [1.1, 2.2, 3.3];

sub check_summation {
    my $precision = shift;
    my ($x, $y, $expected) = @{ $_[0] };
    my $result = $x + $y;

    for my $n ( $x, $y, $expected, $result) {
        $n = sprintf('%.*f', $precision, $n);
    }

    if ( $expected eq $result ) {
        printf "%s + %s = %s\n", $x, $y, $expected;
    }
    else {
        printf "%s + %s != %s\n", $x, $y, $expected;
    }
    return;
}

Output:

1.0 + 2.0 = 3.0
1.1 + 2.2 = 3.3

这篇关于如何修复这个Perl代码,以便1.1 + 2.2 == 3.3?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆