我需要在小数点后用两个数字输出浮点数。此外,我还需要舍入数字。但是,有时我没有得到所需的结果。以下是一个例子。
#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
since1.7/20 = 0.085和1.1/20 = 0.055。从理论上讲,我应该得到0.09和0.06。我知道这与浮点数的二进制表达有关。我的问题是,在固定小数点之后的数字数时,我该如何获得正确的结果?
Edit:这不是另一个问题的重复。使用
fesetround(FE_UPWARD)
无法解决问题。
fesetround(FE_UPWARD)
将圆形(1.0/30)到0.04,而正确的结果应为0.03。此外,fesetround(FE_TONEAREST)
也无济于事。 (1.7/20)仍将圆形到0.08。
Edit2:现在,我知道这种行为可能是由于一半的圆形造成的。但是我怎么避免这种情况呢?也就是说,如果结果确切的一半,则应汇总。是的,您是对的 - 这与基本2中的表示有关,有时基本2值将高于基本10数,有时会更低。但是从来没有太多!
如果您想要更频繁地与期望相匹配的东西,则可以进行两个阶段舍入。 A
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()方法的方法,它将调用
tgmath.h
提供round()方法传递修改值,然后在与相同因子分开后返回值。
问题是由于二进制浮点数表示和C中的浮点数。事实是,1.7和1.1在二进制中不完全表示。 ISO C标准说(我想这在C ++中是相似的):“浮动常数被转换为内部格式,好像在翻译时一样。”这意味着,主动舍入模式(由fesetround
设置)对常数根本不会影响(可能会对运行时发生的圆形产生影响)。
划分20将引入另一个四舍五入错误。根据完整的代码和编译器选项,它可能会在编译时或可能不会完成,因此可以忽略主动的圆形模式。无论如何,如果您期望确切的0.085和0.055,这是不可能的,因为这些值在二进制中不可完全表示。
,即使您有完美的代码可以在2个小数位数上进行回合。在所有情况下都可以工作。
如果您希望能够处理“中点”值,例如0.085,则需要使用一个可以准确表示它们的数字系统,例如十进制算术(但您仍可能在其他类型的操作中遇到舍入错误)。您可能还需要使用以10的功率缩放的整数。没有一般答案,因为这确实取决于应用程序,因为任何解决方法都会有缺点。
有关更多信息,请参见有关浮点的所有一般文章,以及
PDF版本)。