像MS Excel一样在C++中对双值进行舍入
Rounding double values in C++ like MS Excel does it
我在网上到处找,但找不到解决问题的方法。我只是想要一个像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舍入例程,但事实并非如此。你能告诉我我缺了什么吗?
从某种意义上说,你想要的是不可能的:
大多数常见平台上的浮点值没有"小数位数"的概念。像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
是否为非负,或者以其他方式定义负"精度"的语义。)
除了使用浮点点类型,您还可以执行固定点算法。也就是说,您将所有内容存储为整数,但您将它们视为1/1000的单位。然后你可以打印这样一个数字如下:
std::cout << n / 1000 << "." << n % 1000;
加法的作用正如预期的那样,尽管你必须自己编写乘法函数。
要比较双值,必须指定一个比较范围,在该范围内,结果可以被认为是"安全的"。你可以使用一个宏。
以下是您可以使用的一个示例:
#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;
}
感谢大家的回答!在考虑了可能的解决方案后,我将代码中原来的Round()函数改为在值上添加0.6而不是0.5。
值"127827.5"(我知道这不是一个精确的表示!)变成了"127828.1",最后通过floor()将其除以"1.277828"(或更类似于1.2778800..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给出的结果进行了比较。他们都一样。
恐怕答案是Round不能施展魔法
由于1.277828不能准确地表示为double,所以你不能将一些double与1.277828进行比较,并希望它能匹配。
你需要在没有小数部分的情况下进行数学运算,才能得到这些数字。。。这样的事情。
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;
- C++将浮点指针值舍入为小数位数
- Boost::posix_time::ptime舍入到给定的分钟数
- 浮点定向舍入和优化
- 为什么输出精度没有正确舍入?
- 使用设置精度时如何阻止数字向上舍入?
- 如何在使用 MPFR 时在提升多精度中设置舍入模式
- OpenCV 细分一致的 Rect2f 到 Rect2i 舍入
- C++ 中的舍入函数
- 舍入 QDecDouble 值,精度最多为两个字符
- 将大 int 转换为浮点数,而不舍入 c++
- 舍入错误检测
- 根据浮点符号对浮点进行舍入的最简单方法是什么
- 将整数舍入到另一个整数的最接近的倍数
- Sizeof 舍入到对齐方式,但编译器仍将对象放在剩余的字节中
- 避免将 Int 转换为双重类型转换舍入
- 浮点数学运算后舍入不一致
- 浮动到绳子而无需舍入
- C++长双精度格式而不舍入
- 是否可以为 AVR 编写一个 constexpr 舍入函数?
- Time_T舍入到几分钟