在C++中舍入双精度值,就像MS Excel一样

qeeaahzv  于 2023-11-20  发布在  其他
关注(0)|答案(5)|浏览(135)

我找遍了整个网络,但我找不到解决我的问题的方法。我只是想要一个像MS Excel一样对双精度值进行四舍五入的函数。下面是我的代码:

#include <iostream>
#include "math.h"

using namespace std;

double Round(double value, int precision) {
    return floor(((value * pow(10.0, precision)) + 0.5)) / pow(10.0, precision);
}

int main(int argc, char *argv[]) {
    /* The way MS Excel does it:
        1.27815 1.27840 ->  1.27828
        1.27813 1.27840 ->  1.27827
        1.27819 1.27843 ->  1.27831
        1.27999 1.28024 ->  1.28012
        1.27839 1.27866 ->  1.27853
    */
    cout << Round((1.27815 + 1.27840)/2, 5) << "\n"; // *
    cout << Round((1.27813 + 1.27840)/2, 5) << "\n";
    cout << Round((1.27819 + 1.27843)/2, 5) << "\n";
    cout << Round((1.27999 + 1.28024)/2, 5) << "\n"; // *
    cout << Round((1.27839 + 1.27866)/2, 5) << "\n"; // *

    if(Round((1.27815 + 1.27840)/2, 5) == 1.27828) {
                      cout << "Hurray...\n";
    }
    system("PAUSE");
    return EXIT_SUCCESS;
}

字符串
我在stackoverflow找到了这个函数,答案是它像内置的excel舍入例程一样工作,但它不是。你能告诉我我错过了什么吗?

disho6za

disho6za1#

从某种意义上说,你所要求的是不可能的:
大多数通用平台上的浮点值没有“小数位数”的概念。像2.3或8.71这样的数字根本无法精确表示。因此,要求任何函数返回具有给定小数位数的浮点值是没有意义的-这样的数字根本不存在。
对于浮点类型,你唯一能做的就是计算最接近的可表示的近似值,然后以所需的精度打印结果,这将给予你所需的数字的文本形式。要计算表示,你可以这样做:

double round(double x, int n)
{
    int e;
    double d;

    std::frexp(x, &e);

    if (e >= 0) return x; // number is an integer, nothing to do

    double const f = std::pow(10.0, n);
    std::modf(x * f, &d);                 // d == integral part of 10^n * x

    return d / f;
}

字符串
(You也可以使用modf而不是frexp来确定x是否已经是整数。您还应该检查n是否为非负,或者定义负“精度”的语义。)
除了使用 * 浮点 * 类型,你还可以执行 * 定点 * 运算。也就是说,你把所有的东西都存储为 * 整数 *,但是你把它们当作单位,比如说1/1000。然后你可以打印这样一个数字,如下所示:

std::cout << n / 1000 << "." << n % 1000;


加法可以像预期的那样工作,尽管你必须编写自己的乘法函数。

eqzww0vc

eqzww0vc2#

为了比较双精度值,你必须指定一个比较范围,在这个范围内,结果可以被认为是“安全的”。你可以使用宏来实现。
下面是一个用途:

#define COMPARE( A, B, PRECISION ) ( ( A >= B - PRECISION ) && ( A <= B + PRECISION ) ) 

int main()
{
  double a = 12.34567;
  bool equal = COMPARE( a, 12.34567F, 0.0002 );

  equal = COMPARE( a, 15.34567F, 0.0002 );
  return 0;
}

字符串

8e2ybdfx

8e2ybdfx3#

在考虑了可能的解决方案后,我将代码中原始的Round()函数改为将值增加0.6而不是0.5。
值“127827.5”(我知道这不是一个精确的表示!)变成“127828.1”,最后通过floor()除以它变成“1.27828”(或者更像1.2782800..001)。使用Renan Greinert建议的COMPARE,正确选择精度,我现在可以安全地比较这些值。
以下是最终版本:

#include <iostream>
#include "math.h"
#define COMPARE(A, B, PRECISION) ((A >= B-PRECISION) && (A <= B+PRECISION))

using namespace std;

double Round(double value, int precision) {
    return floor(value * pow(10.0, precision) + 0.6) / pow(10.0, precision);
}
int main(int argc, char *argv[]) {
    /* The way MS Excel does it:
        1.27815 1.27840 // 1.27828
        1.27813 1.27840 ->  1.27827
        1.27819 1.27843 ->  1.27831
        1.27999 1.28024 ->  1.28012
        1.27839 1.27866 ->  1.27853
    */
    cout << Round((1.27815 + 1.27840)/2, 5) << "\n"; 
    cout << Round((1.27813 + 1.27840)/2, 5) << "\n";
    cout << Round((1.27819 + 1.27843)/2, 5) << "\n";
    cout << Round((1.27999 + 1.28024)/2, 5) << "\n";
    cout << Round((1.27839 + 1.27866)/2, 5) << "\n";

    //Comparing the rounded value against a fixed one
    if(COMPARE(Round((1.27815 + 1.27840)/2, 5), 1.27828, 0.000001)) {
       cout << "Hurray!\n";
    }
    //Comparing two rounded values
    if(COMPARE(Round((1.27815 + 1.27840)/2, 5), Round((1.27814 + 1.27841)/2, 5), 0.000001)) {
       cout << "Hurray!\n";
    }    

    system("PAUSE");
    return EXIT_SUCCESS;
}

字符串
我测试了一下,取整了100个双精度值,然后将结果与Excel给出的结果进行比较,结果都是一样的。

c0vxltue

c0vxltue4#

恐怕答案是圆圆不能施展魔法
由于1.27828不能完全表示为一个double,你不能将某个double与1.27828进行比较,并希望它匹配。

js5cn81o

js5cn81o5#

你需要做的数学没有小数部分,得到的数字.所以这样的东西。

double dPow = pow(10.0, 5.0);

    double a = 1.27815;
    double b = 1.27840;

    double a2 = 1.27815 * dPow;
    double b2 = 1.27840 * dPow;

    double c = (a2 + b2) / 2 + 0.5;

字符串
使用您的功能...

double c = (Round(a) + Round(b)) / 2 + 0.5;

相关问题