【问题标题】:Surprising float-int conversion in PHPPHP中令人惊讶的浮点整数转换
【发布时间】:2011-08-04 19:04:00
【问题描述】:

花了一个小时试图找出为什么 PHP 脚本给了我不正确的输出后,结果发现在一个特定情况下循环运行了一次迭代。
为了解释发生了什么:从 XML 属性中读取版本号(2 位,例如 1.5 或 2.0)并乘以 100。脚本稍后会在定义的版本号倍数范围内进行迭代。

事实证明,410 == 409,如果您将一个以 10 为增量的计数器与该值进行比较,这会产生一个有趣的惊喜。

这让我想到了我的问题:我是否从根本上理解了错误?当然,4.1100410 都应该可以很好地表示为 float 并且应该可以很好地转换为 int 而不会出现舍入错误?

但是,在我的系统上(使用 PHP 5.3.2 CLI,Zend Engine 2.3.0),以下测试用例

<?
$a = 100 * 4.1;
$b = (string) $a;
$c = (int) $a;
$d = (int)(string) $a;

var_dump($a);
var_dump($b);
var_dump($c);
var_dump($d);
?>

输出:

float(410)
string(3) "410"
int(409)
int(410)

我现在正在做一个 (int)(string) 转换,它有效,但这是一种令人讨厌的 hack,它不漂亮而且感觉不太对。
是否有更好的(正确的、无破解的)解决方案来获得精确的结果?

【问题讨论】:

标签: php floating-point type-conversion


【解决方案1】:

我用这个来解决这个问题:

round($a);

【讨论】:

    【解决方案2】:

    您可以使用 BC 数学函数来获得浮点乘法的可预测结果

    http://php.net/manual/en/ref.bc.php

    intval(4.1 * 100) === 409
    bcmul(4.1, 100) === '410'
    

    【讨论】:

      【解决方案3】:

      如您所见,与 4.1 相乘将返回一个浮点数。将浮点数转换为 int 将执行类似 floor() 的操作,这意味着,如果结果为 409.999... 它将返回 409。如果要使用整数,则不应在计算中混合浮点数和整数值,或者之后你应该使用 round() 来获得最接近的整数。

      精确表示的可能值是 0.5、0.25、0.125,这些值可以通过除以 2 得到,因为该值存储在二进制系统中。

      【讨论】:

        【解决方案4】:

        你的问题在第一行:

        $a = 100 * 4.1;
        

        浮点字面量4.1does not, in fact, have the value 4.1,所以乘以100的结果不是410而是少一点。显然,转换为 string 向上取整,但转换为 int 向下取整。

        要获得您期望的十进制数学,请使用BC Math 函数。

        【讨论】:

        • 在重复“但它确实......”的口头禅一百万次之后,我同意它没有。 :-) 另一方面,由于您提到 int 和 string 显然舍入不同,并且初始转换为 float 是由转换字符串的解释器秘密完成的,所以我想再次转换回字符串的解决方案并不是那么糟糕毕竟。
        • @Damon - 比转换为字符串(不是便宜的操作)然后转换为 int 好得多,将使用 round() 函数。
        【解决方案5】:

        尝试使用intval

        $c = intval($a);
        

        编辑:

        尝试使用普通舍入来舍入它:

        $c = round($a, 0, PHP_ROUND_HALF_UP);
        

        【讨论】:

        • 对我不起作用:$a = 100 * 4.1; var_dump(intval($a));int(409)
        • 很遗憾,intval 的结果相同。
        【解决方案6】:

        您通常看不到一个值是否可以很好地转换为浮点数。阅读The warning in the manual 获取示例。

        【讨论】:

        • 我知道浮点数不能代表每个数字,但据我了解,分别只有 2 位和 3 位十进制数字的数字应该可以用 52 个尾数位表示。这不像我在抱怨 100.00001*41000000000 给出不正确的结果:-)
        • @Damon 十进制有理数不一定是二进制有理数。 0.5 base 10 = 0.1 base 2 这很简单。 0.7 base 10 = 0.101100110011... base 2 因此,固有价值损失,因为将其转换回以 10 为底会产生 0.6999999 左右。 “看起来还不错”不算什么。 :-)
        • @Damon:不,你所做的更像是抱怨 1/3 给你的结果不正确......
        猜你喜欢
        • 1970-01-01
        • 2019-10-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-09
        • 2011-04-29
        • 1970-01-01
        • 2022-06-11
        相关资源
        最近更新 更多