[原]再说精度-用于显示的数字出现精度问题的处理-(Perl/Java)
2005年03月03日写的东西,整理了以前的博客,转到这里来。
描述
1. 昨天在ChinaUnix上看见一个问题:
$ad=1.25 - 1.24; #$ad答案是0.01 #$ad答案应也是0.01,但出现的答案是0。009999999999999979... $ad = 2.25 - 2.24; 为什麽?怎样更正? 谢 |
2. 前段时间刚做完的一个TOYOTA的J2EE项目也遇到类似问题:
把12个字段的字符串转成float再相加,每个字段都是#.##的格式,结果应该等于12.00,可实际是11.9999....,插入到Text里时还出现了越界。
问题
1. 精度:浮点数不能准确的表示
2. 结果的用途:用于计算的数值出现误差时,可能导致计算结果出现很大偏差,即使不会有太大的影响最好也不要冒险;数值是用于显示时,并不一定要处理float使之符合要求,可以尝试处理字符串。本文针对后一种情况。
3. 四舍五入:我简单的翻了一下Perl的函数库,没找到round函数。
解决办法
因为结果是用来显示的,所以并不一定要使float完全符合#.##的形式,可以考虑处理字符串。
由于结果应该符合#.##的形式,也就是说就四舍五入到0.01,但round()[JAVA]和int()[Perl]只能处理整数,所以先将数值乘以100,然后转换为字符串。判断字符串长度,当长度大于1时,在length-2的位置插入“.”,否则从0位置插入“0.0”。
$a = 2.25 - 2.24; # $a = 0.00999999999999979
$a = $a * 100 + 0.5; # $a = 1.49999999999998 # prepare for round off $a = int( $a ); # $a = 1; # now, we got the int value 100 times more than the value wanted. $a = "0.0" . $a # $a = 0.01 # here is the value we wanted. But the value must less than 10. # If we want deal with the value more than 10, we must judging # length of the value and whether append it "0.0" in the front # or insert a "." into the string. print $a; |
输出:0.01
参考
1.MSDN对浮点数精度的描述
《为何浮点数可能丢失精度》
浮点十进制值通常没有完全相同的二进制表示形式。这是 CPU 所采用的浮点数据表示形式的副作用。为此,可能会经历一些精度丢失,并且一些浮点运算可能会产生意外的结果。
导致此行为的原因是下面之一:
十进制数的二进制表示形式可能不精确。
使用的数字之间类型不匹配(例如,混合使用浮点型和双精度型)。
为解决此行为,大多数程序员或是确保值比需要的大或者小,或是获取并使用可以维护精度的二进制编码的十进制 (BCD) 库。
浮点值的二进制表示形式影响浮点计算的精度和准确性。Microsoft Visual C++ 使用 IEEE 浮点格式。
2 四舍五入的算法
四舍五入的算法处理整数,先将数值加0.5,再取得整数部分。
Java中的Math类有round函数,可我在Perl中没找到(可能有)。我想Perl主要是针对字符串处理,也许没有这方面的函数。所幸在Perl中找到int($)函数来截断整数。
所以,在Perl中可以这样做:
$value = int( $old + 0.5 ); |
在Java中直接使用Math.round()。