首页 > 解决方案 > 浮点精度在测试变量是否达到界限时的影响

问题描述

我有一个 C/C++ 函数 checkt (FLT t),其中 FLT 是由预处理器设置的浮点精度,我想在更大的代码体中使用它以确保传递给它的值在 [0,1 ]。它通常用于通过固定增量增加变量 checkt 当参数超出范围时会出错。在这个简单的示例中,我想以 0.1 为增量从 0.0 变为 1.0。但是,由于 0.1 并不能完美地表示为 0.1000000000 等以达到精度极限,因此我将在循环结束时准确地获得值 1.0。如果 FLT 设置为 double,则下面的代码片段有效,但如果设置为 float,则无效。我还附加了两者的输出。编译器为 gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0。

#include <vector>
#include <iostream>

#define FLT double

void checkt (FLT t) {
    if (t < FLT{0} || t > FLT{1}) {
        std::cout << "t before error = " << t << std::endl;
        throw std::runtime_error ("t out of range [0,1]");
    }
}

int main(int argc, char **argv) {
    FLT x = FLT{0.0};
    FLT inc = FLT{0.1};
    FLT y = FLT{1};

    std::cout.precision(16);
    std::cout << "x is " << x << "y is " << y << " inc is " << inc << std::endl;

    for (int i=0; i<11; i++){ 
        std::cout.precision(16);
        std::cout << "x is " << x << std::endl;
        checkt(x);
        x += inc;
    }

    return 0;
}

FLT=double x 的输出是 0y 是 1 inc 是 0.1

x is 0
x is 0.1
x is 0.2
x is 0.3
x is 0.4
x is 0.5
x is 0.6
x is 0.7
x is 0.7999999999999999
x is 0.8999999999999999
x is 0.9999999999999999

FLT = float 的输出是

y is 1 inc is 0.1000000014901161
x is 0
x is 0.1000000014901161
x is 0.2000000029802322
x is 0.300000011920929
x is 0.4000000059604645
x is 0.5
x is 0.6000000238418579
x is 0.7000000476837158
x is 0.8000000715255737
x is 0.9000000953674316
x is 1.00000011920929
t before error = 1.00000011920929
terminate called after throwing an inst

标签: c++loopstestingprecision

解决方案


由于二进制浮点的不精确性质,循环的每次迭代中的重复加法可能会导致误差因子随着时间的推移而累积。

不要x在每次迭代中添加增量,而是将增量乘以循环索引并设置x为该值。

for (int i=0; i<11; i++){
    x = inc * i;
    std::cout.precision(17);
    std::cout << "x is " << x << std::endl;
    checkt(x);
}

输出double

x is 0y is 1 inc is 0.1
x is 0
x is 0.10000000000000001
x is 0.20000000000000001
x is 0.30000000000000004
x is 0.40000000000000002
x is 0.5
x is 0.60000000000000009
x is 0.70000000000000007
x is 0.80000000000000004
x is 0.90000000000000002
x is 1

输出float

x is 0y is 1 inc is 0.1000000014901161
x is 0
x is 0.10000000149011612
x is 0.20000000298023224
x is 0.30000001192092896
x is 0.40000000596046448
x is 0.5
x is 0.60000002384185791
x is 0.69999998807907104
x is 0.80000001192092896
x is 0.90000003576278687
x is 1

推荐阅读