【问题标题】:Reliable Margin of Error for Float -> String -> Float Conversion?浮点->字符串->浮点转换的可靠误差范围?
【发布时间】:2018-01-28 14:45:41
【问题描述】:

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

由于转换,我知道依赖相等会是一个错误,因为可能会丢失精度,所以我正在做类似以下的事情:

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

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

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

换一种说法;给定一个浮点值,我想以 10 为基数存储并回读,我怎样才能可靠地确定我的误差范围应该是多少,以便我可以合理地确认这两个值是相同的?

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

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

【问题讨论】:

  • 我们需要更多关于如何转换为字符串(和返回)的信息。通过正确的转换,您根本不需要任何误差范围。例如,如果您采用有限的 IEEE 754 binary64 浮点数并使用至少 17 位有效数字将其转换为十进制(使用某种形式的四舍五入进行转换),您将得到一个十进制字符串,该字符串转换回您开始使用的 exact 浮动,没有错误。
  • 不幸的是,我认为我仅限于 PHP 的默认浮点到字符串转换;我目前实际上将值存储在 memcached 中,但这似乎是 PHP 的 memcached 模块处理浮点数的方式。

标签: php floating-point precision floating-accuracy


【解决方案1】:

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

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

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

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

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

请注意,%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

【讨论】:

    猜你喜欢
    • 2011-11-25
    • 2014-09-25
    • 2016-01-03
    • 1970-01-01
    • 1970-01-01
    • 2013-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多