浮点数和由于舍入行为导致的不正确结果
Float point numbers and incorrect result due to rounding behavior
我需要输出小数点后有两位数字的浮点数。此外,我还需要把数字四舍五入。然而,有时我得不到我想要的结果。下面是一个例子:
#include <iomanip>
#include <iostream>
using namespace std;
int main(){
cout << setprecision(2);
cout << fixed;
cout<<(1.7/20)<<endl;
cout<<(1.1/20)<<endl;
}
结果如下:
0.08
0.06
,因为1.7/20=0.085和1.1/20=0.055。理论上我应该得到0.09和0.06。我知道它和浮点数的二进制表达式有关。我的问题是,我怎么能得到正确的结果时,固定小数点后的数字与四舍五入?
编辑:这不是一个重复的问题。使用fesetround(FE_UPWARD)
并不能解决问题。fesetround(FE_UPWARD)
将(1.0/30)四舍五入到0.04,而正确的结果应该是0.03。此外,fesetround(FE_TONEAREST)
也没有帮助。(1.7/20)仍接近0.08。
编辑:现在我明白这种行为可能是由于一半到偶数的四舍五入。但是我怎样才能避免这种情况呢?也就是说,如果结果正好是一半,它应该四舍五入。
是的,你是对的-它与以2为基数的表示有关,事实上,以2为基数的值有时会高于以10为基数的数字,有时会低于。但从来没有多过!
如果您想要更频繁地匹配期望,您可以进行两阶段舍入。double
一般精确到至少15位数字(包括小数点左边的数字)。第一次四舍五入会让你得到一个在第二轮四舍五入中更稳定的数字。没有四舍五入会匹配你在十进制中得到的100%的结果,但有可能非常接近。
double round_2digits(double d)
{
double intermediate = floor(d * 100000000000000.0 + 0.5); // round to 14 digits
return floor(intermediate / 1000000000000.0 + 0.5) / 100.0;
}
看到它在行动。
对于一种完全不同的方法,您可以简单地确保以2为基数的数字总是大于所需的小数,而不是在一半的时间里大于一半的时间里小于一半的时间。只需在舍入之前用
nextafter
增加数字的最低有效位。
double round_2digits(double d)
{
return floor(100.0 * std::nextafter(d, std::numeric_limits<double>::max())) / 100.0;
}
您可以自己定义round_with_precision()方法,如果round()方法传递修改后的值,然后除以相同的因子返回值,则调用tgmath.h。
#include <tgmath.h>
double round_with_precision(double d, const size_t &prec)
{
d *= pow(10, prec);
return (std::round(d) / pow(10, prec));
}
int main(){
const size_t prec = 2;
cout << round_with_precision(1.7/20, prec) << endl; //prints 0.09
cout << round_with_precision(1.1/20, prec) << endl; //prints 0.06
}
这个问题是由于c中的二进制浮点表示和浮点常量造成的。事实上,1.7和1.1不能精确地用二进制表示。ISO C标准规定(我想这在c++中也是类似的):"浮动常量被转换为内部格式,就像在翻译时一样。"这意味着活动舍入模式(由fesetround
设置)将对常量完全没有任何影响(它可能对运行时发生的舍入有影响)。
除以20会产生另一个舍入误差。根据完整的代码和编译器选项,它可能在编译时执行,也可能不执行,因此可以忽略活动的舍入模式。在任何情况下,如果您期望精确地0.085和0.055,这是不可能的,因为这些值不能精确地用二进制表示。
因此,即使您有完美的代码,可以将double
值四舍五入为2位十进制数字,这可能无法像您想要的那样工作,因为之前发生了四舍五入错误,并且以一种在所有情况下都有效的方式恢复信息为时已晚。
如果你想处理"中点"对于像0.085这样精确的值,您需要使用能够精确表示它们的数字系统,例如十进制算术(但在其他类型的操作中仍可能出现舍入错误)。您可能还想使用以10为幂的整数。没有通用的答案,因为这实际上取决于应用程序,因为任何变通方法都会有缺点。
有关更多信息,请参阅有关浮点数的所有一般文章和Goldberg的文章(PDF版本)。
- 密码登录程序将永远循环并显示不正确的结果
- 特征 LLT 模块给出不正确的结果?
- 计算幂级数的数学结果不正确
- wcslen() 在使用编译指示包时返回不正确的结果
- 使用动态分配的数组进行矩阵乘法;结果不正确
- CMake 查找特征不正确的结果
- 将函数的地址转换为UINTPTR_T给我不正确的结果
- 使用 C++ 中的运算符重载显示不正确的结果
- 简单的程序比较阵列运行但给出不正确的结果
- 为什么隐式类型转换在此代码中结果不正确
- 使用Boost Polygon的减法结果不正确
- 使用SSE内在时的结果不正确
- 嵌套循环语句结果不正确
- 由于 istream 中的杂散 导致 get () 的结果不正确,但 scanf() 或 cin 则不然
- 主机上运行的自定义函数的Thrust结果不正确
- 使用cblas_dgemv()计算矩阵向量乘积的结果不正确
- 数学库三角函数C++结果不正确
- GLSL -从阴影立方体图中检索值时结果不正确
- 当 T 声明 T::T(T&) 时,std::is_trivially_constructible<T、T&>::value 的结果不正确
- 对于循环数组迭代,if循环测试索引是否为真/假,计算结果不正确