由于舍入行为导致浮点数和不正确的结果

时间:2016-11-02 03:44:49

标签: c++ floating-point rounding

我需要输出小数点后两位数的浮点数。另外,我还需要完善数字。但是,有时我不能得到我需要的结果。以下是一个例子。

#include <iomanip>
#include <iostream>
using namespace std;

int main(){
    cout << setprecision(2);
    cout << fixed;
    cout<<(1.7/20)<<endl;
    cout<<(1.1/20)<<endl;
}

结果是:

0.08
0.06

自1.7 / 20 = 0.085和1.1 / 20 = 0.055。从理论上讲,我应该得到0.09和0.06。我知道它与浮点数的二进制表达式有关。我的问题是如何在修正小数点后四位数后的位数时得到正确的结果?

编辑:这不是另一个问题的重复。使用fesetround(FE_UPWARD)无法解决问题。 fesetround(FE_UPWARD)将舍入(1.0 / 30)到0.04,而正确的结果应为0.03。此外,fesetround(FE_TONEAREST)也没有帮助。 (1.7 / 20)仍然是0.08。

编辑:现在我明白这种行为可能是由于半到三舍五入。但是我怎么能避免这种情况呢?也就是说,如果结果是精确的一半,它应该向上舍入。

2 个答案:

答案 0 :(得分:3)

是的,你是对的 - 它与基数2中的表示有关,有时基数2的值将高于基数10,有时它会更低。但从来没有太多!

如果你想要更频繁地符合期望的东西,你可以做两个阶段的舍入。 double通常精确到至少15位数(总数,包括小数点左边的数字)。您的第一轮舍入将为您提供一个在第二阶段舍入时具有更高稳定性的数字。没有四舍五入将与十进制100%的结果相匹配,但它可能会非常接近。

double round_2digits(double d)
{
    double intermediate = floor(d * 100000000000000.0 + 0.5); // round to 14 digits
    return floor(intermediate / 1000000000000.0 + 0.5) / 100.0;
}

in action

<小时/> 对于完全不同的方法,您可以简单地确保您开始的基数2总是大于所需的小数,而不是大一半的时间和更小的一半时间。在舍入之前,只需使用nextafter递增数字的最低有效位。

double round_2digits(double d)
{
    return floor(100.0 * std::nextafter(d, std::numeric_limits<double>::max())) / 100.0;
}

答案 1 :(得分:2)

您可以定义own round_with_precision()方法,该方法将调用 tgmath.h 提供的 round()方法传递修改后的值,然后在除以相同因子后返回值。

#include <tgmath.h> 
double round_with_precision(double d, const size_t &prec)
{
    d *= pow(10, prec);
    return (std::round(d) / pow(10, prec));
}
int main(){
    const size_t prec = 2;
    cout << round_with_precision(1.7/20, prec) << endl;  //prints 0.09
    cout << round_with_precision(1.1/20, prec) << endl;  //prints 0.06
}