【发布时间】:2016-09-17 19:42:47
【问题描述】:
当我使用代码时:
(sub {
use strict;
use warnings;
print 0.49999999999999994;
})->();
Perl 输出“0.5”。
当我从数字中删除一个“9”时:
(sub {
use strict;
use warnings;
print 0.4999999999999994;
})->();
它打印 0.499999999999999。
只有当我删除另一个 9 时,它才会精确地存储数字。
我知道浮点数是一堆没人愿意处理的蠕虫,但我很好奇 Perl 中是否有办法“捕获”这种隐式转换并死掉,这样我就可以使用 eval 来捕获它死并让用户知道 Perl 不支持他们试图传递的数字的本机形式(因此用户可以传递一个字符串或一个对象)。
我需要这个的原因是为了避免像传递 0.49999999999999994 被我的函数四舍五入这样的情况,但是数字被转换为 0.5,然后被四舍五入为 1 而不是 0。我不知道如何“拦截”这个转换,以便我的函数“知道”它实际上并没有得到 0.5 作为输入,而是拦截了用户的输入。
在不知道如何拦截这种转换的情况下,我不能相信“round”,因为我不知道它是否在我发送它时收到了我的输入,或者该输入是否已被修改(在编译时或运行时,不确定) 在函数被调用之前(反过来,函数不知道它正在操作的输入是否是用户想要的输入,并且无法警告用户)。
这不是 Perl 独有的问题,它发生在 JavaScript 中:
(() => {
'use strict';
/* oops: 1 */
console.log(Math.round(0.49999999999999999))
})();
它发生在 Ruby 中:
(Proc.new {
# oops: 1
print (0.49999999999999999.round)
}).call()
它发生在 PHP 中:
<?php
(call_user_func(function() {
/* oops: 1 */
echo round(0.49999999999999999);
}));
?>
它甚至发生在 C 中(这是可以发生的,但是我的 gcc 并没有警告我该数字没有被精确存储(当指定特定的浮点文字时,它们最好准确存储,否则编译器应该发出警告你决定把它变成另一种形式(例如“你的数字 x 不能以 64 位/32 位浮点形式表示,所以我将它转换为 y。”)所以你可以看看这是否可以,在这个如果不是)):
#include <math.h>
#include <stdio.h>
int main(int argc, char **argv)
{
/* oops: 1 */
printf("%f.\n", round(0.49999999999999999));
return 0;
}
总结:
是否有可能让 Perl 在浮点数的隐式转换上显示错误或警告,或者这是 Perl5(以及其他语言)目前无法做到的事情(例如,编译器不会退出它的)支持此类警告的方式/提供启用此类警告的标志)?
例如
警告:数字 0.49999999999999994 不可表示,已转换为 0.5。使用 bigint 可能会解决这个问题。考虑降低数字的精度。
【问题讨论】:
-
如果他们能把人送上月球……当然有可能,但这将是一个项目。考虑
perl -le 'printf("%.18f\n", 0.49999999999999994-10)'打印-9.500000000000000000,因此IEEE 754 将永远丢失微小的差异,除非您能抓住差异。我认为唯一的捷径是使用 BigNum 或 Math::BigFloat 并将 BigFloat 的完整表示与字符串中的内部 IEEE 754 表示进行比较。这是一个开始。 -
“我的 gcc 没有警告我这个数字没有被精确存储”
-
@Dmitry:浮点值的内部表示几乎总是与其字符串形式具有不同的值。 Perl 会在可能的情况下保留一个整数值,但是一旦你这样做了,比如除法,你可能会得到一个准确的结果 (27/3) 或一个不准确的结果 (2/3)。这仍然归结为您首先对此感兴趣的原因。正如Mark Dickinson suggests 一样,警告每一次违规都没有什么意义。
-
“只有当我删除另一个 9 时,它才会准确地存储数字” 不。只有当你删除那个
9时,打印输出 等于输入字符串。浮点数对于绝大多数应用程序来说足够准确。我认为您更有可能需要更好地了解如何处理代码中的浮点数。 0.49999999999999994 是万亿分之一的精度。这是巨大的,是地球人口的 100,000 倍,你需要做一些专业的事情才能做到这一点。 -
您的实际问题是什么?你认为你为什么需要这个?
标签: perl floating-point