float -> string -> float 转换的可靠误差范围?

Reliable Margin of Error for Float -> String -> Float Conversion?

提问人:Haravikk 提问时间:8/20/2017 最后编辑:Haravikk 更新时间:8/22/2017 访问量:275

问:

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

由于转换,我知道依赖相等将是一个错误,因为可能会失去精度,所以我正在做如下操作:

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

现在,虽然误差的余地对于我的直接目的来说应该没问题,但它让我想知道;我可以可靠/安全地使用的最小误差幅度是多少?0.001

我意识到安全误差幅度会随着浮点数的大小而变化(即较大的值具有较少甚至没有分数精度),因此答案可能应该考虑到这一点。

所以换句话说;给定一个我想以 10 为基数并回读的浮点值,我如何可靠地决定我的误差幅度应该是多少,以便我可以合理地确认这两个值是相同的?

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

编辑:澄清;请假设我的问题是关于处理任意大小的浮子;我给出的示例代码适用于最近一个案例,即我在有限范围内处理浮点数,因此手动设置误差幅度是可以的,但我希望将来能够处理任何量级的浮点数。

php 浮点 精度 float-accuracy

评论

1赞 Mark Dickinson 8/21/2017
我们需要更多关于如何转换为字符串(并返回)的信息。通过正确的转换,您根本不需要任何误差幅度。例如,如果您采用一个有限的 IEEE 754 binary64 浮点数,并使用至少 17 位有效数字将其转换为十进制(使用某种形式的四舍五入到最接近进行转换),则会得到一个十进制字符串,该字符串将转换回您开始使用的确切浮点数,而不会出现错误。
0赞 Haravikk 8/21/2017
不幸的是,我认为我仅限于 PHP 的默认浮点数到字符串转换;我目前实际上将值存储在 memcached 中,但这似乎是 PHP 的 memcached 模块处理浮点数的方式。

答:

1赞 nwellnhof 8/22/2017 #1

正如 Mark Dickinson 的评论中提到的,可以在不损失精度的情况下将浮点数转换为字符串并返回。这仅在以下情况下有效

  • 您使用了足够多的有效十进制数字(IEEE 双精度为 17)
  • 转换是准确的(即保证它们会转换为最接近的数字)

快速浏览一下,似乎在 PHP 中将双精度转换为字符串,无论是隐式的还是 with ,都只使用了 14 位有效数字,所以这种方法不够准确。但是,您可以使用转换说明符来获取 17 位有效数字。所以在下面的往返之后$f(string) $fsprintf%.16e

$s  = sprintf("%.16e", $f);
$f2 = (double) $s;

$f2应该完全相等,除非 PHP 在内部使用次优算法。$f

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

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 的优秀博客