【问题标题】:Is this floating point behavior or a bug in PHP?这是浮点行为还是 PHP 中的错误?
【发布时间】:2014-09-08 23:57:24
【问题描述】:

澄清:这并不是问我为什么会出现舍入错误。我理解这是一个错误或疏忽。问题询问为什么它在第一个 var_dump 中完整打印,但强制转换就像 57916.9repeating 并截断所说的 .9repeating。

会发生以下情况:

您取一个包含值 579.17 的字符串(或浮点数 - 无关紧要)并将其乘以 100。它 var_dumps 预期的 57917。不是 57916.999999999999999999999999 或类似的值。在我看来,var_dump 不应该将任何东西作为调试功能四舍五入。它可能必须截断,但在调试函数中舍入是意外的。

但是,如果随后将其转换为整数,您会从 var_dump 中得到一个意外的 57916。

我知道浮点数的问题,但是在 PHP 中转换一个精确打印为 57917 的浮点数的行为显然有效地减去了 1。这是一个非常小的数字。

这似乎只发生在某些数字上,例如 579.17。我测试过的其他人不会发生这种情况。我们所做的只是将一个数字乘以 100 以发送到需要美分的 API。 API 库可以理解地转换为整数,因为 API 不接受小数美分。

测试用例:

php -r '$n = ("579.17" * 100); var_dump($n, (int)$n);'

输出:

float(57917)
int(57916)

环境:

x86-32,
x86-64 both.

【问题讨论】:

  • 谢谢。我知道这样的浮点怪事。我不明白的是 var_dump 打印 57917,而不是像 57916.999999999999999999999999999 这样的奇怪东西。当我施放它时,它似乎是。没有什么要求 var_dump 进行舍入。我永远不会期望 var_dump 舍入任何东西。就像小数一样,FP 不能代表所有内容,这是代码中将美元+美分更改为美分的疏忽,但我发现这种确切的情况令人费解,因为它在演员表之后打印出来就好像它是完整的一样。

标签: php debugging floating-point


【解决方案1】:

var_dump 使用 php.ini 中的 precision 来显示浮点值。你可以举起它看看会发生什么。

php -r 'ini_set("precision", 20); $n = ("579.17" * 100); var_dump($n, (int)$n);'
// double(57916.999999999992724)
// int(57916)

还有。没有x86或x64。 PHP 使用 64 位的浮点数。

http://php.net/manual/en/language.types.float.php

【讨论】:

  • 嗯。如此有效地 PHP is 在您打印它时对其进行四舍五入,并且在“精度”设置中消耗分辨率所需的多少个 9 后它会进行四舍五入?但是当你施放时它不会这样做。除非我遗漏了什么,否则我认为 var_dump() 在任何情况下都不应该四舍五入。这是一个调试功能。
  • 是的。显示时用于舍入的精度。
  • 已接受并 +1。我仍然不确定 var_dump() 是否应该进行任何舍入。浮点数可以表示某些数字,而其他数字则不能。这比通常的浮点运算更混乱。
  • @JaimieSirovich:如果var_dump() 根本不进行四舍五入,它最终会显示(例如)0.1000000000000000055511151231257827021181583404541015625 用于0.11.00000000000000001999189980260288361964776078853415942018260300593659569925554346761767628861329298958274607481091185079852827053974965402226843604196126360835628314127871794272492894246908066589163059300043457860230145025079449986855914338755579873208034769049845635890960693359375E-100 用于1e-100。我怀疑这真的是用户想要的。
  • @JaimieSirovich ,关于金钱和浮动有一个规则。不要在花车里存钱。
【解决方案2】:

使用round() 代替int()579.17 * 100 的实际值类似于 57916.99999var_dump() 显示为 57917,但是当您使用 int() 时,它会截断分数。使用round() 将转到最接近的整数,而不是总是向下截断。

【讨论】:

  • 这实际上就是我们最终所做的。我想知道这是否会以任何语言发生并且是特定于硬件的。这特别奇怪,因为它只发生在某些数字上,但不是全部,而且 var_dump 不会打印 .9repeating。
  • 它是浮点特有的,并且会在大多数语言中发生。它只发生在某些数字上的原因是它们中的一些有偏高的错误,所以它们可能是 57817.0000001,它按您的预期四舍五入。
  • 阅读上面在 cmets 中发布的链接。
  • 我以前遇到过 FP 问题。我知道你不能依赖平等。我从来没有见过一个没有四舍五入的整数打印,但是当你投射它时却不是。也许我只是没有注意到。此示例不在指南中,而且非常违反直觉。
  • 当你打印一个浮点数时,它试图通过打印具有相同内部表示的最简单的数字来帮助它,这会导致它被四舍五入。这隐藏了这样的不准确性。
【解决方案3】:

我相信这是因为硬件无法真正准确地表达浮点数。所以显示为 579.17 实际上更像是 579.16999999。因此,当您将其相乘并将其转换为 int 时,它会截断小数点,留下 57916。

【讨论】:

  • 我在 C 中有一个类似的错误,只有以 4 结尾的数字会导致此错误,但没有其他问题。
  • 当它是一个浮点数时它实际上是否打印为一个整数,然后在你投射时只导致四舍五入的怪异?这就是这里最奇怪的地方。
  • 是的,就是这样。确实很奇怪。
猜你喜欢
  • 2011-07-08
  • 1970-01-01
  • 2019-08-11
  • 2012-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-09
相关资源
最近更新 更多