perl 不一致的负零结果

发布于 2024-12-09 10:14:50 字数 193 浏览 2 评论 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; 

当我运行脚本时,结果是 -0.0 而不是 0.0 有人可以解释为什么以及如何将其更改为 0.0。

I have the following code :

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; 

when I run the script the result is -0.0 and not 0.0 could someone explain why and how i can change it to be 0.0.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

花海 2024-12-16 10:14:50

首先,这与 Perl 无关。返回 -0.0 的是您的处理器。您会在其他语言中看到相同的行为。


你问为什么,大概是在问为什么这有用。老实说,我不知道。一些科学家和工程师可能会利用它。

+0.0 表示“零或正值稍大一些”。

-0.0 表示“零或负值稍大一些”。


您还询问如何摆脱该标志。

负零为假,所以 $x || 0 就可以了。

First, this has nothing to do with Perl. It's your processor that's returning -0.0. You'll see this same behaviour in other languages.


You ask why, presumably asking why this is useful. Honestly, I don't know. Some scientists and engineers probably take advantage of it.

+0.0 would indicate "zero or something very slightly larger on the positive side".

-0.0 would indicate "zero or something very slightly larger on the negative side."


You also ask how to get rid of the sign.

Negative zero is false, so $x || 0 does the trick.

丶视觉 2024-12-16 10:14:50

你遇到了一些非常奇怪的事情。我的第一个想法是,您看到了一些非常小的负数,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 code> 正在执行时计算,并且计算产生不同的结果。 编辑:删除;用函数调用替换所有常量的程序的修改版本具有相同的行为。

我可以通过在 printf 调用之前添加这些行来避免负零显示:

$x += 0.0;
$y += 0.0;

但这很丑陋。

(顺便说一句,大约一个月前,我使用 Perl 5.15.2 的“尖端”版本得到了相同的结果。)

类似的 C 程序为 x 和 y 打印 -0.000000

编辑:进一步的实验表明,负整数值乘以 0.0 得到 0.0,但负非整数值乘以 0.0 得到 -0.0。我已经提交了 Perl 错误报告

You've run into something very strange. My first thought was that you were seeing some very small negative number that sprintf rounds to -0.0, but in fact the result of the expression is an actual negative zero.

Here's a simpler program that exhibits the same issue:

#!/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;

and the output:

x = 0.000000
y = -0.000000

My best guess is that -1.0 * 0.0 is being computed at compile time, but -1.5 * 0.0 is being computed at execution time, and the computations are yielding different results. EDIT: Strike that; a modified version of the program that replaces all the constants with function calls has the same behavior.

I can avoid the negative zero display by adding these lines before the printf calls:

$x += 0.0;
$y += 0.0;

but that's ugly.

(Incidentally, I get the same results with the "bleading-edge" version of Perl 5.15.2 from about a month ago.)

A similar C program prints -0.000000 for both x and y.

EDIT: Further experiment shows that multiplying a negative integral value by 0.0 yields 0.0, but multiplying a negative non-integral value by 0.0 yields -0.0. I've submitted a Perl bug report.

痴情换悲伤 2024-12-16 10:14:50

这里没什么可看的,继续走吧……

零由指数 emin - 1 和零尾数表示。
由于符号位可以采用两个不同的值,因此有两个
零、+0 和 -0。

http://download.oracle.com/docs/cd /E19957-01/806-3568/ncg_goldberg.html

Nothing to see here, move along...

Zero is represented by the exponent emin - 1 and a zero significand.
Since the sign bit can take on two different values, there are two
zeros, +0 and -0.

http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html

南风几经秋 2024-12-16 10:14:50

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;

Data::Float has some useful information as well as routines to check if a floating point value is zero.

The short answer is, when dealing with floating point, you cannot assume algebraic identities will be preserved.

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;
青春如此纠结 2024-12-16 10:14:50

这并没有直接解决这篇文章,但它确实解决了 perl 中存在的“奇怪”行为。


(我相信)导致此问题的原因是 Perl 将数字转换为整数,然后使用 INTEGER/ALU 数学而不是 FP/FPU 数学。然而,没有 -0 整数[以二进制补码表示]——只有一个 -0 整数,它实际上是一个浮点值——所以浮点值 -0.0 被转换为整数 0 < em>在乘法之前:-)

这是我的“演示”:

printf "%.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 数学!);-)

This doesn't address the post directly, but it does address the "odd" behavior that exists in perl.


(I believe) This issue is caused because perl is converting the numbers to integers and then using INTEGER/ALU math instead of FP/FPU math. However, there is no -0 integer [in two's complement] -- only an -0 integral which is really a floating point value -- so the floating point value -0.0 is converted to the integer 0 before the multiplication :-)

Here is my "demonstration":

printf "%.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;

And my "result/reasoning" is:

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

Happy musings.


Python does not exhibit these quirks because it has strongly-typed numbers: it will not convert an integral floating point value to an integer prior to a math operation. (Python will still perform standard type-widening.) Try divide by 0.0 (FP, not INTEGER math!) in perl ;-)

下雨或天晴 2024-12-16 10:14:50

很奇怪。我注意到,如果将 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

Very strange. I note that the problem disappears if you replace 1.5 with a negative integer:

$ 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

All I can think of is to treat -0.0 as a special case:

my $ans = (0.6*$m+ 0.7 * $e-1.5)*$g;
my $x=  sprintf("%0.1f", $ans == -0.0 ? 0.0 : $ans)

(EDIT: This was a dumb suggestion, since -0.0 == 0.0.)

I also checked Python's behaviour, which consistently retains the sign, which suggests that the negative sign is not really a bug in Perl, just a little strange (though I'd say that treating integers and non-integers differently is a bug):

$ 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
无畏 2024-12-16 10:14:50

答案:使用绝对值函数,abs()

Code

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;

Answer: use the absolute value function, abs()

Code

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 standard

abs(x) copies a floating-point operand x to a destination in the same
format, setting the sign bit to 0 (positive).

EDIT (addressing Justin's feedback):

my $result = possible_negative_zero();
$result = abs($result) if $result == 0.0; # because -0.0 == 0.0
printf "%f\n", $result;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文