【问题标题】:Rounding a float to two decimal places将浮点数四舍五入到小数点后两位
【发布时间】:2016-09-08 21:47:01
【问题描述】:
我希望将浮点值(例如 val)四舍五入到最接近的 0.05 倍数。我的意图的解释是here。我已经有了val 的上限和下限,分别是valU 和valL。我可以通过以下方式做到这一点:
- 在
[valL, valU] 范围内搜索最接近的0.05 倍数并相应地分配值。平局是通过采取较低的价值来解决的。或
- 使用 this 之类的东西(在链接中给出的解决方案中,将 20 替换为 100)。
我发现方法 2 有时会产生错误的结果。有人可以告诉我为什么方法 1 是正确的方法吗?
【问题讨论】:
标签:
c
floating-point
rounding
【解决方案1】:
...将float 值四舍五入,例如val 到最接近的0.05 倍数...
鉴于典型的binary floating point,可以拥有的最好的是
...将float 值四舍五入到最接近0.05 的倍数R 并保存为最接近的可表示float r。
方法#1 在极端情况下不清楚。 OP 所拥有的不是代码,而是从数学角度来看的代码大纲,而不是实际的 C 代码。我会选择 #2 的变体,非常效果很好。 @Barmar
几乎是方法 2:乘法、舍入、然后除法。
#include <math.h>
double factor = 20.0;
float val = foo();
double_or_float rounded_val = round(val * factor)/factor;
这种方法有两个微妙的地方使它更胜一筹。
与参考答案相比,乘法的精度和范围更大 - 这允许 精确 乘积和 非常 精确商。如果仅使用float 数学计算乘积/商,则某些极端情况最终会得到错误的答案,当然某些大值会溢出到无穷大。
-
“平局以较低的价值结算。”是一个艰难而不同寻常的目标。听起来像是一个旨在扭曲选择的目标。 round(double) 无论当前的舍入方向如何,都可以很好地从零开始舍入一半的情况。要完成“降低”,请更改当前舍入方向并使用rint() 或nearbyint()。
#include <fenv.h>
int current_rounding_direction = fegetround();
fesetround(FE_DOWNWARD);
double rounded_val = rint(val * factor)/factor;
fesetround(current_rounding_direction);
...方法 2 有时会产生错误的结果...
OP 需要发布代码和 exact 使用和计算的值,以便对各种方法的优势/劣势进行质量解释。试试printf("%a %a\n", val, rounded_val);。通常,由于对代码使用printf("%f\n", val);所使用的确切值的理解不准确而出现问题@
进一步:“我已经有了val 的上限和下限,分别是valU 和valL。我可以通过以下方式做到这一点:”
这可能是准确的,因为 valU 和 valL 的偏差只是原始问题的迭代 - 找到 rounded_val。查找valL 和valU 的代码都需要一个上限和下限,否则如何防止[valL ... valU] 范围本身具有不准确的端点?