比较小数的最佳方法是什么?
假设我有 2 个值,比如3.45
and 3.44
,可靠比较它们的最佳方法是什么?
我正在考虑将所有数字存储为345
,344
以便我只比较整数,并且只向用户显示带小数点的格式化数字。
另一种解决方案是使用自定义函数来测试差异,当差异小于数字时0.01
,数字应该相等。
还有哪些其他可能的解决方案(更好的解决方案)?
比较小数的最佳方法是什么?
假设我有 2 个值,比如3.45
and 3.44
,可靠比较它们的最佳方法是什么?
我正在考虑将所有数字存储为345
,344
以便我只比较整数,并且只向用户显示带小数点的格式化数字。
另一种解决方案是使用自定义函数来测试差异,当差异小于数字时0.01
,数字应该相等。
还有哪些其他可能的解决方案(更好的解决方案)?
最常见的技术是使用 epsilon(您描述的第二件事)。但是,制作一个适用于所有输入数字的通用 epsilon 可能非常困难/不可能。如果您正在处理 0.00001 左右的数字或 1000000000 左右的数字,那么 0.01 的 epsilon 对您来说可能会很糟糕。阅读本文以获得对 epsilon 技术的全面分析。
您描述的第一个解决方案在时间数学中非常常见。一切都以整数刻度表示。刻度可以代表 1 秒或 1 毫秒,或任何你想要的。然后,您可以根据需要将它们转换为另一个单位的小数,或进行比较。唯一的事情是您确实需要选择一个刻度大小,并且没有任何东西可以表示小于 1 个刻度单位。
这也称为“模糊比较”,允许两个值略有不同(容差,也称为“epsilon”)。通常,这样的 epsilon 值约为1E-6
,1E-10
但您会发现更小或更大的值更适合的应用程序:在您的示例中,epsilon 不应小于1E-2 = 0.01
。
一旦找到适合您需要的 epsilon 值,您就可以编写以下一组比较函数,如下所示(用 C 和 C++ 的公共子集编写;它们应该适用于几乎所有面向对象/过程语言,只需稍作更改) :
const double fuzzyEpsilon = 1E-6; // just an example!
bool fuzzyEqual(double a, double b) {
return (abs(a - b) <= fuzzyEpsilon);
}
bool fuzzyUnqual(double a, double b) {
return (abs(a - b) > fuzzyEpsilon);
}
int fuzzyCompare(double a, double b) {
return ((a - b) > fuzzyEpsilon) - ((b - a) > fuzzyEpsilon);
}
第三个函数分别通过模糊比较(类似于)返回-1
, 0
, 1
if a < b
,a == b
的代码。该实现假定编程语言将布尔值隐式转换为(false) 和(true)。如果没有,请使用以下内容:a > b
strcmp
0
1
int fuzzyCompare(double a, double b) {
return (a - b) > fuzzyEpsilon ? 1 :
((b - a) > fuzzyEpsilon ? -1 : 0);
}
用二进制实数表示十进制值是近似的,导致各种奇怪的行为。精度通常会随着进一步的算术而降低,尤其是附近值的减法。但是,可以通过在比较它们之前四舍五入到最小位数来清理单个值以进行比较。例如 V = round (V * 1e14)/1e14 将任何值 V 舍入为 14 位十进制数字。可以自信地比较两个这样的值是否相等。64 位实数具有 15.65 小数精度,因此四舍五入到 14 位(或更少)提供了一些错误空间。
是的,乘法、舍入()和除法序列很昂贵。然而,十进制是一种人机界面,通常用于不能容忍“有趣”算术的货币应用程序。犯错通常比缓慢更糟糕。