int((0.1+0.7)*10) = 7 几种语言。如何防止这种情况?

IT技术 php javascript python ruby internal-representation
2021-01-23 10:52:05

最近我遇到了几种语言的错误/功能。我对它是如何引起的有一个非常基本的了解(我想要一些详细的解释),但是当我想到这些年来我必须犯的所有错误时,问题是我如何确定“嘿,这可能会导致一个可笑的错误,我最好使用任意精度函数“,其他语言确实有这个错误(以及那些没有的,为什么)。另外,为什么 0.1+0.7 会这样做而 0.1+0.3 不会,还有其他众所周知的例子吗?

PHP

//the first one actually doesn't make any sense to me,
//why 7 after typecast if it's represented internally as 8?
debug_zval_dump((0.1+0.7)*10); //double(8) refcount(1)
debug_zval_dump((int)((0.1+0.7)*10)); //long(7) refcount(1)
debug_zval_dump((float)((0.1+0.7)*10)); //double(8) refcount(1)

Python:

>>> ((0.1+0.7)*10)
7.9999999999999991
>>> int((0.1+0.7)*10)
7

Javascript:

alert((0.1+0.7)*10); //7.999999999999999
alert(parseInt((0.7+0.1)*10)); //7

Ruby:

>> ((0.1+0.7)*10).to_i                                                  
=> 7                                                                    
>>((0.1+0.7)*10)                                                       
=> 7.999999999999999                                                    
6个回答
谢谢,这正是我要找的文章。
2021-03-30 10:52:05
通过提供此链接作为问题的答案,获得了多少 SO 代表永远不会让我感到惊讶。尽管进行了所有搜索和重复追捕,但这个问题以各种伪装不断被问到
2021-03-31 10:52:05
是的,这正是我想要的,而不仅仅是“数字被截断......”。
2021-04-05 10:52:05
它很长而且不是很容易阅读,但你一生中只需要理解一次这些东西:)
2021-04-09 10:52:05

这不是语言问题。这是浮点运算的一般问题

是的,这是一个语言问题。语言设计者本可以为数字文字选择更合适的默认解释。例如,在 Fortress 中,0.7它只是有理数的语法糖7/10(注意:这是数字“十分之七”,而不是“七除以十”计算)。而且,缺省基数是十进制的,不是二进制。这几乎可以 100% 解决几乎每个人都遇到的问题。(注意:显然,真正的计算机没有无限内存,因此即使是 Fortress 也必须在某些时候截断和舍入。但不适用于诸如金钱或 OP 之类的情况。)
2021-04-09 10:52:05
@Jörg:有Decimal类型,可以根据需要使用我认为在通用语言中设置默认值没有意义。
2021-04-12 10:52:05

停止使用浮动不完全是。

数字浮点表示并不准确

在 Python 中,int将浮点数向零截断到最接近的整数。 (int)在 PHP、parseIntJavascript 和to_iRuby 中做同样的事情。

这不是错误;这就是这些功能的工作方式。

例如,来自Python 的文档int

将浮点数转换为整数会截断(向零)。

这是一个与浮点表示有关的已知问题,您可以在此处找到更多信息:

http://en.wikipedia.org/wiki/IEEE_754-2008

具体的问题是7.9在转换为int时会直接转换(trunc)为7。在 Python 中,您可以使用以下方法解决此问题:

int( round(((0.1+0.7)*10)) )

......在其他语言中也是如此。

但是,是的,这在许多情况下都是一个问题。例如,浮点数对于薪资程序来说不够可靠。

也许其他人可以给你其他提示。无论如何,这会有所帮助。