my $m=0;
my $e =0 ;
my $g=0;
my $x= sprintf( "%0.1f", (0.6*$m+ 0.7 * $e-1.5)*$g);
print $x;
当我运行脚本时,结果为-0.0而不是0.0,请问有人能解释一下为什么以及如何将其更改为0.0吗?
my $m=0;
my $e =0 ;
my $g=0;
my $x= sprintf( "%0.1f", (0.6*$m+ 0.7 * $e-1.5)*$g);
print $x;
你遇到了一些非常奇怪的事情。我最初的想法是你看到了一些非常小的负数,sprintf
将其四舍五入为-0.0,但实际上表达式的结果是实际的负零。
这里有一个更简单的程序,也出现了相同的问题:
#!/usr/bin/perl
use strict;
use warnings;
my $x = -1.0 * 0.0;
my $y = -1.5 * 0.0;
printf "x = %f\n", $x;
printf "y = %f\n", $y;
以及输出:
x = 0.000000
y = -0.000000
-1.0 * 0.0
,但是在执行时计算了-1.5 * 0.0
,这些计算结果不同。 编辑: 划去上面的内容; 将所有常量替换为函数调用的修改版本具有相同的行为。printf
调用之前添加以下代码来避免负零的显示:$x += 0.0;
$y += 0.0;
但那很丑陋。
(顺便说一下,我使用大约一个月前的“最新版”Perl 5.15.2得到了相同的结果。)
类似的C程序打印出-0.000000
,x和y都一样。
编辑:进一步实验表明,将负整数值乘以0.0会产生0.0,但将负非整数值乘以0.0会产生-0.0。我已经提交了一个Perl错误报告。
1.5
替换为负整数,则问题会消失:$ perl -e '
my @a=(-9.0, -3.0, -2.0, -1.5, -1.2, -1.0, -0.8, -0.5);
for my $a (@a) {
$bin = join("", map {sprintf("%02x", ord($_))} split(//, pack("d>", $a*0)));
printf("%4.1f * 0 = %4.1f %s\n", $a, $a*0, $bin);
}'
-9.0 * 0 = 0.0 0000000000000000
-3.0 * 0 = 0.0 0000000000000000
-2.0 * 0 = 0.0 0000000000000000
-1.5 * 0 = -0.0 8000000000000000
-1.2 * 0 = -0.0 8000000000000000
-1.0 * 0 = 0.0 0000000000000000
-0.8 * 0 = -0.0 8000000000000000
-0.5 * 0 = -0.0 8000000000000000
我所能想到的就是将 -0.0 视为一种特殊情况:
my $ans = (0.6*$m+ 0.7 * $e-1.5)*$g;
my $x= sprintf("%0.1f", $ans == -0.0 ? 0.0 : $ans)
(编辑:这是一个愚蠢的建议,因为-0.0 == 0.0
。)
我还检查了Python的行为,它始终保留符号,这表明负号并不是Perl中的一个错误,只是有点奇怪(尽管我认为将整数和非整数区分对待是一个错误):
$ python -c '
for a in [-9.0, -3.0, -2.0, -1.5, -1.2, -1.0, -0.8, -0.5]:
print "%0.1f" % (a*0,)
'
-0.0
-0.0
-0.0
-0.0
-0.0
-0.0
-0.0
-0.0
my $f = 0.0; print "neg zero\n" if $f == -0.0;
输出 neg zero
。 - musiKkprintf "%.f\n", 2.0 * -0.0;
printf "%.f\n", 1.5 * -0.0;
printf "%.f\n", 1.0 * -0.0;
printf "%.f\n", 1e8 * -0.0;
printf "%.f\n", 1e42 * -0.0;
我的“结果/推理”是:
0 # 2.0 -> 2 and -0.0 -> 0: INTEGER math
-0 # 1.5 is not an integral: FP math, no conversions
0 # 1.0 -> 1 and -0.0 -> 0: INTEGER math
0 # 1e8 -> 100000000 and -0.0 -> 0: INTEGER math
-0 # 1e42 is an integral OUTSIDE the range of integers: FP math, no conversions
愉快的思考。
Python没有这些怪癖,因为它具有强类型数字:在数学运算之前,它不会将整数浮点值转换为整数。(Python仍将执行标准类型扩展。)尝试在perl中除以0.0(FP,而不是INTEGER math!)
Data::Float包含一些有用的信息,以及检查浮点值是否为零的例程。
简短的回答是,在处理浮点数时,您不能假设代数恒等式将被保留。
use strict;
use warnings;
use Data::Float qw(float_is_zero);
my $m = 0;
my $e = 0;
my $g = 0;
my $result = (0.6 * $m + 0.7 * $e - 1.5) * $g;
$result = 0.0 if float_is_zero($result);
my $x = sprintf( "%0.1f", $result);
print $x;
这里没有什么可看的,继续前进...
零由指数 emin - 1 和零有效数字表示。 由于符号位可以取两个不同的值,因此有两个零,+0 和 -0。
http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html
abs()
代码printf "%f\n", -0.0;
printf "%f\n", abs(-0.0);
Perl 5.10.1
-0.000000
0.000000
Perl 5.12.1
-0.000000
0.000000
Perl 6(rakudo-2010.08)
0.000000
0.000000
IEEE 754标准
abs(x)将浮点操作数x复制到相同格式的目标中,将符号位设置为0(正数)。
编辑(回应Justin的反馈):
my $result = possible_negative_zero();
$result = abs($result) if $result == 0.0; # because -0.0 == 0.0
printf "%f\n", $result;