我正在尝试编写好的round_double函数,它将以指定的精度舍入double:
1
double round_double(double num, int prec)
{
for (int i = 0; i < abs(prec); ++i)
if(prec > 0)
num *= 10.0;
else
num /= 10.0;
double result = (long long)floor(num + 0.5);
for (int i = 0; i < abs(prec); ++i)
if(prec > 0)
result /= 10.0;
else
result *= 10.0;
return result;
}
2
double round_double(double num, int prec)
{
double tmp = pow(10.0, prec);
double result = (long long)floor(num * tmp + 0.5);
result /= tmp;
return result;
}
这个功能做了我想做的事,但在我看来,它们还不够好。因为从精确度= 13 - 14开始,它们会返回不良结果。
原因我确信有可能编写好的double_round只是通过cout
以指定的精度(比如18)打印数字,打印效果比我的函数效果好。
例如这部分代码:
int prec = 18;
double num = 10.123456789987654321;
cout << setiosflags(ios::showpoint | ios::fixed)
<< setprecision(prec) << "round_double(" << num << ", "
<< prec << ") = " << round_double(num, prec) << endl;
将第一个round_double(10.123456789987655000, 18) = -9.223372036854776500
打印round_double
,第二个打印round_double(10.123456789987655000, 18) = -9.223372036854776500
。
如何在c ++中编写好的round_double函数?或者已经存在?
答案 0 :(得分:2)
不要强制转换为强制转换为范围有限的整数的long long
,超过10 ^ 13要求的范围(对于64位而没有整数部分,则为19)。只需拨打floor
即可。
double round_double(double num, int prec)
{
double tmp = pow(10.0, prec);
double result = floor(num * tmp + 0.5);
result /= tmp;
return result;
}
请注意,迈克也是正确的,你有一个有限的范围,你可以代表双重本身。如果你需要干净的十进制响应,那就没那么好了。但是long long
是你完全古怪的数字的原因。
答案 1 :(得分:1)
问题是浮点表示。二进制表示不能精确地表示所有十进制数,并且只有有限的精度。
double
通常表示由IEEE754指定的64位二进制表示,具有52位小数部分。这给出了大约16位十进制数的精度。
如果你需要更高的精度,那么最好的选择可能是使用任意精度的算术库,例如GMP。您的编译器可能提供或不提供精度高于long double
的{{1}}类型。
编辑:抱歉,我没有注意到您的结果完全不正确。正如另一个答案所说,这是由于转换为double
溢出。
答案 2 :(得分:0)
另一种方法是基于精度的二进制数进行舍入。下面的示例实现 - 不确定它是否对您有用,但是因为您让我玩,我以为我会把它扔出去。
注意:
1位精度的输入数(10.1234 ...)为8; 2,等于10等。
另外,恕我直言的十进制舍入最好在输出时或使用支持十进制的表示(例如存储int
尾数和10次幂指数)时完成。
#include <ieee754.h>
#include <iostream>
#include <iomanip>
double round_double(double d, int precision)
{
ieee754_double* p = reinterpret_cast<ieee754_double*>(&d);
std::cout << "mantissa 0:" << std::hex << p->ieee.mantissa0
<< ", 1:" << p->ieee.mantissa1 << '\n';
unsigned mask0 = precision < 20 ? 0x000FFFFF << (20 - precision) :
0x000FFFFF;
unsigned mask1 = precision < 20 ? 0 :
precision == 53 ? 0xFFFFFFFF :
0xFFFFFFFE << (32 + 20 - precision);
std::cout << "masks 0:" << mask0 << ", 1: " << mask1 << '\n';
p->ieee.mantissa0 &= mask0;
p->ieee.mantissa1 &= mask1;
std::cout << "mantissa' 0:" << p->ieee.mantissa0
<< ", 1:" << p->ieee.mantissa1 << '\n';
return d;
}
int main()
{
double num = 10.123456789987654321;
for (int prec = 1; prec <= 53; ++prec)
std::cout << std::setiosflags(std::ios::showpoint | std::ios::fixed)
<< std::setprecision(60)
<< "round_double(" << num << ", " << prec << ") = "
<< round_double(num, prec) << std::endl;
}
...输出
mantissa 0:43f35, 1:ba76eea7
masks 0:fff80000, 1: 0
mantissa' 0:0, 1:0
round_double(10.123456789987654858009591407608240842819213867187500000000000, 1) = 8.000000000000000000000000000000000000000000000000000000000000
mantissa 0:43f35, 1:ba76eea7
masks 0:fffc0000, 1: 0
mantissa' 0:40000, 1:0
round_double(10.123456789987654858009591407608240842819213867187500000000000, 2) = 10.000000000000000000000000000000000000000000000000000000000000
mantissa 0:43f35, 1:ba76eea7
masks 0:fffe0000, 1: 0
mantissa' 0:40000, 1:0
round_double(10.123456789987654858009591407608240842819213867187500000000000, 3) = 10.000000000000000000000000000000000000000000000000000000000000
mantissa 0:43f35, 1:ba76eea7
masks 0:ffff0000, 1: 0
mantissa' 0:40000, 1:0
round_double(10.123456789987654858009591407608240842819213867187500000000000, 4) = 10.000000000000000000000000000000000000000000000000000000000000
mantissa 0:43f35, 1:ba76eea7
masks 0:ffff8000, 1: 0
mantissa' 0:40000, 1:0
round_double(10.123456789987654858009591407608240842819213867187500000000000, 5) = 10.000000000000000000000000000000000000000000000000000000000000
mantissa 0:43f35, 1:ba76eea7
masks 0:ffffc000, 1: 0
mantissa' 0:40000, 1:0
round_double(10.123456789987654858009591407608240842819213867187500000000000, 6) = 10.000000000000000000000000000000000000000000000000000000000000
mantissa 0:43f35, 1:ba76eea7
masks 0:ffffe000, 1: 0
mantissa' 0:42000, 1:0
round_double(10.123456789987654858009591407608240842819213867187500000000000, 7) = 10.062500000000000000000000000000000000000000000000000000000000
mantissa 0:43f35, 1:ba76eea7
masks 0:fffff000, 1: 0
mantissa' 0:43000, 1:0
round_double(10.123456789987654858009591407608240842819213867187500000000000, 8) = 10.093750000000000000000000000000000000000000000000000000000000
mantissa 0:43f35, 1:ba76eea7
masks 0:7ffff800, 1: 0
mantissa' 0:43800, 1:0
round_double(10.123456789987654858009591407608240842819213867187500000000000, 9) = 10.109375000000000000000000000000000000000000000000000000000000
mantissa 0:43f35, 1:ba76eea7
masks 0:3ffffc00, 1: 0
mantissa' 0:43c00, 1:0
round_double(10.123456789987654858009591407608240842819213867187500000000000, a) = 10.117187500000000000000000000000000000000000000000000000000000
mantissa 0:43f35, 1:ba76eea7
masks 0:1ffffe00, 1: 0
mantissa' 0:43e00, 1:0
round_double(10.123456789987654858009591407608240842819213867187500000000000, b) = 10.121093750000000000000000000000000000000000000000000000000000
mantissa 0:43f35, 1:ba76eea7
masks 0:fffff00, 1: 0
mantissa' 0:43f00, 1:0
round_double(10.123456789987654858009591407608240842819213867187500000000000, c) = 10.123046875000000000000000000000000000000000000000000000000000
etc....