检查是否以特定精度给出双精度

Check if double are given with a specific accuracy

本文关键字:双精度 精度 是否 检查      更新时间:2023-10-16

我有一个double的向量。这些数字以诸如0.001之类的精度给出,这意味着这些数字以千分之一单位的精度给出。例如,我希望有123456789.012但不是123456789.01231.在实践中,由于浮点运算,实际数字更像是123456789.01199999452这是123456789.012的有效和最接近的表示。

我想有力地测试我的数字是否真的以应有的准确性给出。例如,我想警告一下,如果我的123456789.01231与准确性不匹配0.001.对我来说,另一个有效的选择是找到给定数字的准确性。

到目前为止,我所做的是测试(x - offset)/accuracy是否是整数。偏移可防止整数溢出。在实践中,由于浮点运算,它不能是整数,所以我添加了一个任意阈值。它有效,但在我看来不是一个强大的解决方案。

dx = (x[i]-offset)/accuracy;
ix = std::round(dx);
if (std::abs(dx - ix) > 1e-5)
throw exception("...");

此过程不涉及任何字符串。这些数字来自二进制文件或运行时计算。准确性由用户给出,并且数字应符合此准确性。在我之前的123456789.012示例中,我知道这个数字实际上并不存在,但123456789.01199999452有效,因为它是它与double的最佳表示。但是123456789.01231(实际上是123456789.01230999827)是无效的,因为它不是3位舍入值的最佳表示形式。

您似乎希望确保您的数字尽可能接近一组数字,这些数字是从 0 到无穷大的算术级数,具有精度值的共同差异。在这种情况下,这基本上是一个量化任务,需要进一步评估量化误差。

#include <cmath>
#include <iostream>
bool is_number_accurate(double n, double accuracy)
{
bool accurate = false;
std::cout << "n: " << n << ", ";
n = std::abs(n);
if (n > accuracy)
{
// Epsilon to account for precision loss from the calculations below,
// as well as floating point representation error.
const double epsilon = 0.00001;
// Remainder from quantizing n to accuracy precision
// (i.e. quantization error)
double rem = std::fmod(n, accuracy);
// Normalize to 1.0. Here 0 means n exactly matches the quantized value,
// and slightly greater than 0 means that n is slightly greater than the
// quantized value.
// Values close to 1.0 also mean n is close to match the quantized value,
// but is slightly less than it.
double error = rem / accuracy;
std::cout << "error: " << error << ", ";
accurate = error < epsilon || (error <= 1.0 && error > (1.0 - epsilon));
}
else
{
accurate = n == 0.0 || n == accuracy;
}
std::cout << "accurate: " << accurate << std::endl;
return accurate;
}
int main()
{
is_number_accurate(0.0, 0.001);
is_number_accurate(0.0000000000001, 0.001);
is_number_accurate(0.001, 0.001);
is_number_accurate(0.01, 0.001);
is_number_accurate(123456789.012, 0.001);
is_number_accurate(123456789.0121, 0.001);
is_number_accurate(123456789.0125, 0.001);
is_number_accurate(123456789.0129, 0.001);
is_number_accurate(123456789.013, 0.001);
is_number_accurate(123456789.01231, 0.001);
}

输出为:

n: 0, accurate: 1
n: 1e-13, accurate: 0
n: 0.001, accurate: 1
n: 0.01, error: 0, accurate: 1
n: 1.23457e+08, error: 0.999992, accurate: 1
n: 1.23457e+08, error: 0.0999936, accurate: 0
n: 1.23457e+08, error: 0.5, accurate: 0
n: 1.23457e+08, error: 0.899992, accurate: 0
n: 1.23457e+08, error: 0.999994, accurate: 1
n: 1.23457e+08, error: 0.309996, accurate: 0

问题中提出的问题表明有人试图以非设计的方式使用浮点数,但没有足够的信息来建议替代方案。因此,有些不情愿,这是如上所述的问题的解决方案。

/*  IsNearestValue(x, A) returns true iff x is the double number nearest to the
nearest multiple of A', where A' is the unit fraction nearest A.  (All
negative powers of 10, such as 10**-3, are unit fractions.)
For example, IsNearestValue(x, .001) returns true iff x is the result of
converting some number with three decimal digits after the decimal point
to double.
This routine presumes x/A <= 2**53.
*/
bool IsNearestValue(double x, double A)
{
//  Calculate 1/A'.  The result has no rounding error.
double reciprocal = std::round(1/A);
/*  Find what multiple of A' x must be near.  This produces an exact
result.  That is, t is an integer such that t * A' = x, with
real-number arithmetic, not floating-point arithmetic.
*/
double t = std::round(x * reciprocal);
//  Calculate the double nearest t/A'.
t /= reciprocal;
//  Return true iff x is the double nearest t/A'.
return x == t;
}

这里的概念非常简单。首先,我们纠正了A作为double给出的问题,但没有一个期望的数字(.1,.01,.001)不能用double表示。但是,它们的倒数可以,因此我们取倒数和舍入以准确获得所需数字的倒数。

然后我们将x乘以倒数并四舍五入到最接近的整数。然后我们将该整数除以倒数,这给了我们x必须double,如果它确实是最接近的double所需A的某个乘数。

我不确定约束x/A≤ 253是否必要,但我没有试图证明它不是,所以除非有进一步的要求,否则我将离开该约束。