我需要输出小数点后两位的浮点数。另外,我还需要对数字进行四舍五入。但是,有时我得不到我需要的结果。下面是一个例子。
#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。
编辑:现在我明白了,这种行为可能是由于二分之一到偶数的舍入。但我如何才能避免这种情况呢?也就是说,如果结果是精确的一半,它应该四舍五入。
3条答案
按热度按时间fcy6dtqo1#
是的,你是对的--这与以2为基数的表示有关,而且有时以2为基数的值会比以10为基数的值高,有时会比以10为基数的值低,但绝不会高很多!
如果你想得到更符合预期的结果,你可以做两个阶段的舍入。(总计,包括小数点左边的数字)。第一次舍入将为第二阶段的舍入留下更稳定的数字。任何舍入都不会与十进制100%的结果相匹配,但也有可能非常接近。
看它in action。
对于完全不同的方法,您可以简单地确保以2为基数的数字总是大于所需的小数,而不是一半时间大于一半时间小于一半时间。在舍入之前,只需将数字的最低有效位递增
nextafter
。x8diyxa72#
您可以定义own的**round_with_precision()方法,该方法将调用 tgmath.h 提供的round()**方法传递修改后的值,然后返回除以相同因子后的值。
mrzz3bfm3#
这个问题是由于C语言中的二进制浮点表示和浮点常量造成的。事实上,1.7和1.1并不能精确地用二进制表示。ISO C标准说(我想这在C++中也是类似的):“浮点常量被转换为内部格式,就像在转换时一样。”这意味着活动的舍入模式(由
fesetround
设置)对常量没有任何影响(它可能对运行时发生的舍入有影响)。除以20将引入另一个舍入错误。根据完整的代码和编译器选项,它可能在编译时完成,也可能不在编译时完成,因此活动的舍入模式可能被忽略。在任何情况下,如果您期望精确的0.085和0.055,这是不可能的,因为这些值不能精确地用二进制表示。
因此,即使您有完美的代码将
double
值四舍五入为2个十进制数字,这也可能无法按您希望的方式工作,因为之前发生过舍入错误,而且要以在所有情况下都有效的方式恢复信息也为时已晚。如果希望能够精确处理“中点”值(如0.085),则需要使用可以精确表示它们的数字系统,如十进制算术(但在其他类型的运算中仍可能会出现舍入误差)。您可能还希望使用按10的幂缩放的整数。没有通用的答案,因为这实际上取决于应用程序,因为任何解决方法都有缺点。
有关更多信息,请参阅所有关于浮点和Goldberg's article(PDF version)的一般性文章。