【问题标题】:Check if double are given with a specific accuracy检查是否以特定的精度给出了双精度
【发布时间】:2020-06-02 14:34:43
【问题描述】:

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

我想有力地测试我的数字是否真的具有应有的准确性。例如,如果我的123456789.012310.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位四舍五入值的最佳表示。

【问题讨论】:

  • 乘以 1000 并检查小数部分是否符合阈值 - 例如添加 0.001 并检查结果是否小于 0.01,但不确定实际值。
  • 假设这些数字是以文本形式“给出”的,你可以在转换前读取字符串并验证格式。
  • 你想要做的是违背浮点数的本质。不仅数字不能用浮点数准确表示,实际精度取决于数字大小。您正在寻找的可能是一种专为满足您的目的而设计的定点格式。 en.wikipedia.org/wiki/Fixed-point_arithmetic
  • @JRR 以防万一您想检查计算的数学错误,这与您描述的任务完全不同。在这种情况下,您可以将 FP 数分解为符号、指数和尾数,并测试尾数的符号、指数和 N 个最高有效位是否等于预期值。这里,N 表示所需的精度。
  • 我会看看 x 和 std::nexttoward(x,0) 之间的区别,并检查它是否小于 1/512。这样,您就会知道,即使该值没有准确表示,它也由该类型可以容纳的最接近的数字表示。

标签: c++ floating-point floating-accuracy integer-arithmetic


【解决方案1】:

您似乎希望确保您的数字尽可能接近一组数字,这些数字是从 0 到无穷大的arithmetic progression,具有精度值的公差。在这种情况下,这基本上是一个quantization task,并进一步评估了量化误差。

#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

【讨论】:

    【解决方案2】:

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

    /*  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;
    }
    

    这里的概念很简单。首先,我们纠正了Adouble 形式给出的问题,但没有一个所需的数字(.1、.01、.001)可以用double 表示。但是,它们的倒数可以,所以我们取倒数和四舍五入,得到所需数的倒数。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-11
      • 2021-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-07
      • 2011-11-09
      相关资源
      最近更新 更多