【问题标题】:PHP cast float to string is different from cast to intPHP将浮点数转换为字符串与转换为int不同
【发布时间】:2019-01-17 15:46:49
【问题描述】:

当你在 php 中处理浮点数时你不会想到但需要注意的事情

<?php


    $i = (32.87*100);

    echo $i;                 // outputs 3287
    echo (int) $i;           // outputs 3286 !!
    echo (int) (string) $i   // outputs 3287 

$i 的内部表示类似于 3286.9999999。

为什么$i3287的字符串表示形式?

【问题讨论】:

  • 如果您在上一个示例中更改强制转换的顺序,您将得到 3286。 echo (string) (int) $i;
  • 一个真正令人印象深刻的问题,通过指出内部价值来展示一些研究和背景知识。所有这些都是新贡献者提出的第一个问题。非常好!

标签: php


【解决方案1】:

让我们看看你的代码:

$i = (32.87*100);

现在$i 比3287 略小,如下所示:

echo sprintf('%.30f', $i) . PHP_EOL; //3286.999999999999545252649113535881

但是当你打印(回显)它时,你会得到四舍五入的值。

echo $i;                 // outputs 3287

这里我们来了个窍门 - 将 float 转换为 int 意味着简单地切断点之后的部分,尽管它的 .99999999(...) 几乎是 1(但它不是!)。所以输出是 3286。

echo (int) $i;           // outputs 3286 !!

现在,在最后一个示例中,您首先将 float 转换为字符串,这意味着您已经通过执行 echo $i; 所做的事情,因为无论您打印什么,PHP 内部都需要转换为字符串。所以它是将3286.999999999999545252649113535881 转换为"3287",然后转换为3287,然后打印。

echo (int) (string) $i   // outputs 3287;

综上所述,float 转换为 string 和 int 的方式不同。

编辑关于“四舍五入”的进一步解释

嗯,它并不是真正的四舍五入。我这样说是错误的。

PHP 使用 64 位浮点数(也称为 double),在十进制表示中具有 14 位精度。

如PHP manual中所述:

float 的大小取决于平台,尽管最大约为 1.8e308 且精度约为 14 位十进制数字是常用值(64 位 IEEE 格式)。

这意味着,浮点数可以包含(在大多数情况下)一个 14 位数字(十进制),并且点的位置无关紧要。

现在,最重要的是:

转换为字符串不会对浮点数进行四舍五入

例子:

  • $a = 1.23456789012349 - 最后一个 9 是第 15 位,所以你会得到“四舍五入”的浮点数到 1.2345678901235
  • $a = 12345678901234.9 - 同上
  • $a = 1.99999999999999 - 最后一个 9 是第 15 位,所以你会得到 2

作为一个字符串,它将完全按照浮点数打印,这意味着 14 位精度。 “四舍五入”是在我们在内存中创建浮点变量结构的那一刻。

最后一个例子就是我们在这个话题中讨论的内容。

现在,为什么我犯了那个错误并说“四舍五入”? 我误解了echo sprintf('%.30f', $i) 的结果。 A 看到更多的数字,并认为这是浮点数的实际值。

但事实并非如此。

众所周知,64 位浮点数只有 14 位精度。

那么sprintf的结果从何而来?

答案其实很简单。

我们已经知道,在二进制系统中表示十进制数并不总是可能的。因此,例如一个简单的 0.1 in float(二进制表示)只是一个近似值,因为真正的二进制表示将无限长。

现在将二进制系统转换为十进制时,它的工作原理完全相同。可以用二进制表示的东西(这意味着每个浮点值),并不总是可以用十进制表示。

那么sprintf('%.30f', $i) 是给出将浮点数从二进制转换为十进制的 30 位精度近似值。

感谢@Quasimodo'sclone 在评论中要求更准确地说明这一点。这让我对这个话题有了更深入的了解。

【讨论】:

  • 这更详细地解释了问题中已经说过的内容,但并没有完全回答“为什么 $i 3287 的字符串表示形式?”这个问题。究竟在哪种情况下强制转换为字符串?考虑(string) 1.999;
  • @Quasimodo'sclone 给你。查看更新。希望它解释得足够深入。
  • 更深一点,我们几乎无法到达这里。
  • 我多次阅读这个编辑过的答案。这应该至少投票+50。而且这个问题应该得到很高的投票率,因为它不是每天都会出现的问题,甚至显示一些研究指出了内部价值。我不明白为什么它得到了一张反对票。
  • 感谢您提供如此广泛而清晰的解释。
【解决方案2】:

您将 $i (3287) 转换为字符串,然后转换为 int,因此结果保持为 3287。 如果您将$i 转换为 int,您将得到 3286,然后如果您将其转换为字符串,您将得到您想要的。

试试echo (string) (int) $i

【讨论】:

    猜你喜欢
    • 2011-11-25
    • 2011-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多