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

问题描述 投票:0回答:6

我在网上进行了搜索,但找不到解决我的问题的方法。我只是想要一个像 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 舍入例程,但事实并非如此。你能告诉我我错过了什么吗?

c++ excel double rounding
6个回答
2
投票

从某种意义上说,你所要求的是不可能的:

大多数常见平台上的浮点值没有“小数位数”的概念。像 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;
}

(您还可以使用

modf
代替
frexp
来确定
x
是否已经是整数。您还应该检查
n
是否为非负数,或者以其他方式定义负“精度”的语义。)


除了使用 floating 点类型之外,您还可以执行 fixed 点算术。也就是说,您将所有内容存储为“整数”,但将它们视为 1/1000 等单位。然后你可以打印这样一个数字,如下所示: std::cout << n / 1000 << "." << n % 1000;

加法按预期工作,尽管您必须编写自己的乘法函数。


2
投票

以下是您可以使用的一个示例:

#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; }



1
投票

值“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; }

我通过四舍五入一百个双精度值并将结果与 Excel 给出的结果进行比较来对其进行测试。他们都是一样的。


0
投票
由于 1.27828 不能完全表示为双精度数,因此您不能将某个双精度数与 1.27828 进行比较并希望它能够匹配。



0
投票

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;



0
投票
https://stackoverflow.com/a/28638557/21365

在 C++ 中实现它可能遇到的困难是您需要十进制(以 10 为基数的浮点)实现。

© www.soinside.com 2019 - 2024. All rights reserved.