我有一个float
值,我需要在PHP中将其作为字符串存储,然后在转换回浮点后进行比较。
由于转换,我知道依赖平等将是一个错误,因为有可能导致精度损失,所以我做了类似以下的事情:
if (abs((float)$string_value - $float_value) < 0.001) { echo "Values are close enough\n"; }
现在,虽然0.001
的错误余量对于我的直接目的应该没问题,但它让我感到疑惑;我能可靠/安全地使用的最小误差幅度是多少?
我意识到安全误差范围会随着浮点数的大小而变化(即较大的值具有较小的甚至没有分数精度),因此答案应该可以解释为此。
所以换一种方式;给定一个浮点值,我希望存储在基数10并回读,我怎样才能可靠地确定我的误差范围应该是什么,以便我可以合理地确认这两个值是否相同?
不幸的是,我处理的值必须以纯十进制形式存储,所以我通常将它们打包为网络顺序64位整数不是一个选项☹ ️
编辑:澄清;请假设我的问题是处理任意大小的花车;我给出的示例代码是针对最近我在有限范围内处理浮动的情况,因此手动设置误差范围很好,但我希望能够处理浮点数在未来任何规模。
答案 0 :(得分:0)
正如Mark Dickinson的评论中所提到的,可以将浮点数转换为字符串并返回而不会丢失精度。这只适用于
从快速看一下,似乎在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。