有没有更好的方法在C ++中用n个小数舍入浮点数?

时间:2017-11-16 07:55:59

标签: c++

我知道常见的方法是乘以10 ^ n然后除以10 ^ n。 round a float with one digit number 但是由于双精度精度问题,我发现上面的解决方案并不能完全适用于我的情况:

0.965 * 0.9 = 0.8685

std::round(0.8685 * 1000) / 1000 = 0.869
// expects it to be 0.869
std::round(0.965 * 0.9 * 1000) / 1000 = 0.868

如果我想直接从0.869获取std::round(0.965 * 0.9 * 1000) / 1000,我必须将声明更改为

std::round((0.965 * 0.9 + std::numeric_limits<double>::epsilon()) * 1000) / 1000 = 0.869

是否有更简单的方法进行舍入而不为每次计算添加epsilon

编辑:问题是,直觉上std::round(0.965 * 0.9 * 1000) / 1000的值应为0.869(因为0.965 * 0.9 = 0.8685),但它实际上会给0.868。我想找到常用的方法,用3位小数精度得到精确的数学值。

2 个答案:

答案 0 :(得分:7)

这实际上是一个令人惊讶的非平凡问题,除非您的问题仅涉及格式输出,在这种情况下,您可以使用适当的printf样式格式化程序,或{{ 1}}操纵者。

从根本上说,使用二进制类型的denary舍入没有多大意义。您的具体问题是由于最接近的IEEE754 ostreamdouble

0.965

您的第一个停靠点是学习Is floating point math broken?。希望这会说服你添加一个任意的“epsilon”只是将问题转移到其他输入,并且你使用0.96499999999999996891375531049561686813831329345703125 也没有数字理由。

std::numeric_limits<double>::epsilon()完美地工作,部分原因是整数std::round的截止点x.5是二元有理数,因此可以用二进制浮点精确表示。但是,唉,你不能用它来舍入到任意小数点。

如果您愿意忍受偶尔出现的不良后果,那么您目前正在使用的x成语可能是您最好的选择。

如果您不能忍受任何虚假错误,那么您可能需要使用十进制类型并对其执行舍入功能。见C++ decimal data types

答案 1 :(得分:0)

您可以专门为该计算创建一个新功能:

#include <iostream>
#include <limits>
#include <cmath>

using namespace std;

double roundDec(double var) {
    var = round((var + std::numeric_limits<double>::epsilon()) * 1000) / 1000;

    return var;
}

int main() {

    double var = 0.8685;

    cout << roundDec(var);

    return 0;
}