由于舍入行为

问题描述 投票:0回答:3

我需要在小数点后用两个数字输出浮点数。此外,我还需要舍入数字。但是,有时我没有得到所需的结果。以下是一个例子。

#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
c++ floating-point rounding
3个回答
3
投票
通常至少准确至至少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()
方法的方法,它将调用

tgmath.h

提供
round()
方法传递修改值,然后在与相同因子分开后返回值。

2
投票

问题是由于二进制浮点数表示和C中的浮点数。事实是,1.7和1.1在二进制中不完全表示。 ISO C标准说(我想这在C ++中是相似的):“浮动常数被转换为内部格式,好像在翻译时一样。”这意味着,主动舍入模式(由fesetround设置)对常数根本不会影响(可能会对运行时发生的圆形产生影响)。 划分20将引入另一个四舍五入错误。根据完整的代码和编译器选项,它可能会在编译时或可能不会完成,因此可以忽略主动的圆形模式。无论如何,如果您期望确切的0.085和0.055,这是不可能的,因为这些值在二进制中不可完全表示。 ,即使您有完美的代码可以在2个小数位数上进行回合。在所有情况下都可以工作。 如果您希望能够处理“中点”值,例如0.085,则需要使用一个可以准确表示它们的数字系统,例如十进制算术(但您仍可能在其他类型的操作中遇到舍入错误)。您可能还需要使用以10的功率缩放的整数。没有一般答案,因为这确实取决于应用程序,因为任何解决方法都会有缺点。 有关更多信息,请参见有关浮点的所有一般文章,以及

Goldberg的文章(
PDF版本
)。

0
投票

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.