如何检查两个数字是否在浮点类型的精度限制的有效数字"x"范围内?
How can I check whether two numbers are within "x" significant figures of the precision limits of the floating point type?
所以假设我们有一个浮点类型XType,其中有两个数字:
XType const a = 1.2345
XType const b = 1.2300
然后我想要一个函数IsClose(XType常量f1,XType常量f2,无符号常量truncated_files),这样
// the numbers are equal if the last two figures are ignored (1.23 == 1.23)
IsClose<XType>(a,b,2) == true
// the numbers are not equal if only the last is ignored (1.234 != 1.230)
IsClose<XType>(a,b,1) == false
到目前为止,我有一个丑陋的烂摊子,但我还没有说服自己这是正确的:
// check if two floating point numbers are close to within "figures_tolerance" figures of precision for the applicable type
template <typename FloatType>
bool const IsClose(FloatType const f1, FloatType const f2, unsigned const figures_tolerance)
{
FloatType const tolerance_exponent = std::pow(10.0,figures_tolerance);
FloatType const tolerance =
std::pow(tolerance_exponent,std::log10(f1)) *
std::numeric_limits<FloatType>::epsilon()
;
return std::abs(f1 - f2) < tolerance;
}
我的推理是,公差应该是提升到数字超过或小于1.0的数量级的ε(ε所基于的有效数字)。这有道理吗?有没有更好、更可靠的方法?
编辑:我使用模板功能的解决方案如下(它基于下面用户763305的回答)
// check if two floating point numbers are within the last n digits of precision for the
// largest of the two numbers being compared.
template <typename FloatType>
bool const IsWithinPrecision(FloatType const f1, FloatType const f2, unsigned const n = 1U)
{
FloatType const f_ref = std::max(std::abs(f1), std::abs(f2));
FloatType const distance = std::abs(f1 - f2);
FloatType const e = std::numeric_limits<FloatType>::epsilon();
return distance < std::pow((FloatType) 10.0, (FloatType) n) * e * f_ref;
}
要测试两个数字是否在彼此的n
有效位数内,请使用不等式
abs(a - b) < pow(0.1, n) * max(abs(a), abs(b))
然而,我通常发现测试有效位数是否至少是最大可能的有效位数(给定浮点类型的精度)减去n
更有用。这可以使用不等式来完成
abs(a - b) < pow(10.0, n) * std::numeric_limits<...>::epsilon() * max(abs(a), abs(b))
换言之,n
是我们通过舍入误差丢失的有效位数。类似n = 2
或3
的东西通常在实践中有效。
这样做的原因是浮点数a
和a
以下和以上的下一个可表示浮点数之间的距离位于之间
0.5 * std::numeric_limits<...>::epsilon() * abs(a)
和
std::numeric_limits<...>::epsilon() * abs(a)
此外,如果你处理的是非常小的,或者更准确地说,非正规化的数字,那么上述不等式就不起作用。那么你应该使用不等式
abs(a - b) < pow(10.0, n) * max(
std::numeric_limits<...>::epsilon() * max(abs(a), abs(b)),
std::numeric_limits<...>::denorm_min()
)
由于这只是为了调试,因此可能会松懈,并使用简单的相对错误测试,例如:
if (fabs(f1 - f2) <= SomeNumber * fabs(f2)) ThingsAreGood else ThingsAreBad;
这假设f2
是已知的良好(或至少已知更好)值,并且浮点运算中舍入的误差与f2
成比例。请注意,计算可能会以复杂的方式产生错误。例如,如果在f1
上加上和减去各种其他值,使得中间值具有比由f2
表示的最终结果大得多的幅度,则舍入误差可能与那些大的中间值成比例,而不是与f2
成比例。在这种情况下,您可能需要根据中间计算而不是f2
来计算错误阈值。
考虑到Eric Postpischil指出的情况,该函数根据精度告诉两个数字是否足够接近。
bool const IsClose(FloatType const f1, FloatType const f2, unsigned const figures_tolerance)
{
FloatType res = f1-f2;
res = res*pow(10.0,figures_tolerance);
return !bool(int(res));
}
编辑答案中使用的解决方案在我进行大量比较的情况下不起作用。
我写了一个函数,使用字符串与给定数字精度的进行比较
#include <iomanip>
/**
* Compare two number with a given digit precision
*
* @tparam T - Number precision
*
* @param n1 - First number to compare
* @param n2 - Second number to compare
* @param n - The first n digits that must be equals between the two numbers
*
* @return True if the n first digits of the two numbers are equals, false otherwise
*/
template<typename T>
bool isEqual(T n1, T n2, int n)
{
int index = 0;
std::ostringstream a, b;
a << std::setprecision(n);
b << std::setprecision(n);
std::cout << std::setprecision(n);
a << std::fixed;
b << std::fixed;
std::cout << std::fixed;
a << n1;
b << n2;
while (a.str()[index] == b.str()[index] && index < n) {
index++;
}
if (index != n) {
std::cout << "n1 != n2nnn1 = " << a.str() << "nn2 = " << b.str() << "ndiffer at index " << index << std::endl;
}
return index == n;
}
- 为什么在传递长整型时调用具有两个双精度类型的参数的重载函数?
- if(双精度类型 == 字符类型)
- 为什么乘法与C++双精度类型值的加法一样快?
- 如何将双精度类型的鼠标移动 x 和 y 从主机传递到 Qemu 中的虚拟机
- 解释为什么 C++ 和 C# 对双精度类型具有不同的最大值?
- 由于C++中双精度类型的准确性而导致的错误
- 由将值分配给双精度类型的变量而导致的浮点异常
- odeint:复杂且更高的精度类型
- 如何替换具有双精度类型的模板参数
- 在双精度类型数组中使用 memset()
- 如何将双精度类型转换为 DWORDLONG 或 DWORD 转换为 DWORDLONG 或 DWORD_PTR 转换为
- 无法将值赋给双精度类型
- 使用分数正确初始化双精度类型
- g++ abs()在短整型上似乎将其转换为双精度类型
- 常数表达式可以是双精度类型而不是只能是整型吗?
- 将c++双精度类型*转换为Java双精度类型
- 请求从"双精度"类型转换为非标量类型以及其他一些错误
- 无法从不区分大小写的字符串中提取双精度类型
- 如何将双精度类型转换为char*类型
- 复杂/双精度类型的运算符重载"+"