Float的可靠误差范围 - >字符串 - >浮动转换?

时间:2017-08-20 11:27:57

标签: php floating-point precision floating-accuracy

我有一个float值,我需要在PHP中将其作为字符串存储,然后在转换回浮点后进行比较。

由于转换,我知道依赖平等将是一个错误,因为有可能导致精度损失,所以我做了类似以下的事情:

if (abs((float)$string_value - $float_value) < 0.001) { echo "Values are close enough\n"; }

现在,虽然0.001的错误余量对于我的直接目的应该没问题,但它让我感到疑惑;我能可靠/安全地使用的最小误差幅度是多少?

我意识到安全误差范围会随着浮点数的大小而变化(即较大的值具有较小的甚至没有分数精度),因此答案应该可以解释为此。

所以换一种方式;给定一个浮点值,我希望存储在基数10并回读,我怎样才能可靠地确定我的误差范围应该是什么,以便我可以合理地确认这两个值是否相同?

不幸的是,我处理的值必须以纯十进制形式存储,所以我通常将它们打包为网络顺序64位整数不是一个选项☹ ️

编辑:澄清;请假设我的问题是处理任意大小的花车;我给出的示例代码是针对最近我在有限范围内处理浮动的情况,因此手动设置误差范围很好,但我希望能够处理浮点数在未来任何规模。

1 个答案:

答案 0 :(得分:0)

正如Mark Dickinson的评论中所提到的,可以将浮点数转换为字符串并返回而不会丢失精度。这只适用于

  • 您使用足够的有效十进制数字(IEEE双打为17)
  • 转化准确无误(即保证转换为最接近的数字)

从快速看一下,似乎在PHP中将一个双$f转换为字符串,无论是隐式还是(string) $f,只使用14位有效数字,因此这种方法并不准确足够。但是,您可以使用sprintf%.16e转化说明符来获得17位有效数字。所以经过以下往返

$s  = sprintf("%.16e", $f);
$f2 = (double) $s;
除非PHP在内部使用次优算法,否则

$f2应完全等于$f

请注意,%e转换说明符使用科学(指数)表示法。如果需要简单的十进制字符串,可以使用%f说明符并使用log10计算小数点后所需的位数:

if ($f != 0) {
    $prec = 16 - floor(log10(abs($f)));
    if ($prec < 0) $prec = 0;
}
else {
    $prec = 0;
}
$s = sprintf("%.${prec}f", $f);

但是,对于非常小或大的数字,这会产生非常长的字符串。

可能需要大量的研究来判断这些方法是否完全可靠,如果不是最大误差的话。这完全取决于PHP版本,底层C库等几个实现细节。

另一个想法是比较字符串表示而不是浮点值:

# Assuming $string_value was also converted with float_to_string
if ($string_value == float_to_string($float_value)) {
    echo "Values are close enough\n";
}

只要您坚持使用相同的PHP版本,这应该是可靠的。

如果必须比较浮点数,那么比较相对误差通常更有意义。有关详细信息,请参阅Bruce Dawson's excellent blog