C中的printf如何对浮点数进行舍入?

Posted

技术标签:

【中文标题】C中的printf如何对浮点数进行舍入?【英文标题】:How does printf in C round floating-point numbers? 【发布时间】:2019-06-11 14:33:04 【问题描述】:

我正在尝试实现printf,我想知道printf 如何舍入浮点数,因为我找不到一般规则

如果例如输入 => printf("|%.f| |%.1f| |%.2f| |%.5f| |%.12f", 0.000099, 0.000099, 0.000099, 0.000099, 0.000099);

这是输出 => |0| |0.0| |0.00| |0.00010| |0.000099000000

我使用来自 IEEE-754 的方法,所以我们在内存中的浮点数是:0.000098999999999999994037755413067714016506215557456016540527343750

我的问题是我应该何时以及如何舍入我的浮点数?

我正在寻找所有浮点数必须遵循的一般规则。

【问题讨论】:

factmonster.com/math-science/mathematics/… 以何种方式舍入出乎意料?这正是我将它四舍五入的方式。 printf 重新编码好运,这不适合初学者。先尝试更简单的事情 你应该什么时候四舍五入?当他格式化字符串需要它时。你应该怎么圆?到具有所要求精度的最接近的值。 如果您在示例中看到我们的结果为 0.00010 并且我们在舍入之前的浮点数是 0.00009 我们可以说 9 舍入到 10 我们写 0 并保留 1 以将其添加到 0 这给了我们0.00010 但这条规则并不通用 【参考方案1】:

不确定这是否正是 printf 的做法,但这似乎适用于您的示例:

加5/(10^(小数点数+1)。然后截断。

您的解释是有缺陷的:您的 C 编译器将您的常量向上转换为双精度值。所以它没有使用0.0000989999.... 它使用的是更准确的双重等价物。

试试这个:

 printf("|%.f| |%.1f| |%.2f| |%.5f| |%.12f", (float)0.000099, (float)0.000099, (float)0.000099, (float)0.000099, (float)0.000099);

输出:

|0| |0.0| |0.00| |0.00010| |0.000098999997

【讨论】:

谢谢你的回答,这是我的错,是我做了 double 而不是 float,但在这种情况下这不是问题,因为在这里我想知道 printf 如何管理 float 或 double 或 long double 的舍入 不涉及向上转换:常量 double-constants.【参考方案2】:

您试图解决的问题实际上很难正确解决。

许多现有的printf 实现在大约 30 年前使用conversion code dtoa.c written by David M. Gay

您可以从这个问题中了解更多信息,这不是完全重复的:

Why does "dtoa.c" contain so much code?

还有这些网站:

Rick Regan 的博客文章https://www.exploringbinary.com/quick-and-dirty-floating-point-to-decimal-conversion/ 克林格的How to Read Floating Point Numbers Accurately David M. Gay 的论文,Correctly Rounded Binary-Decimal and Decimal-Binary Conversions。 大卫·戈德堡的What Every Computer Scientist Should Know About Floating Point Arithmetic。

【讨论】:

感谢您的回答我已经重新编码 printf 并成功重新编码 float、double 和 long double 多亏了 ieee-754 方法,但在这里我仍然没有找到我应该四舍五入的一般规则在我的代码中实现【参考方案3】:

C 标准在 §7.21.6.1p13 中提供以下内容:

对于e, E, f, F, g, and G 转换,如果有效小数位数最多为DECIMAL_DIG,则结果应正确舍入。如果有效小数位数大于DECIMAL_DIG,但源值可以用DECIMAL_DIG 数字精确表示,则结果应该是带有尾随零的精确表示。否则,源值由两个相邻的十进制字符串L<U 界定,两者都具有DECIMAL_DIG 有效数字;生成的十进制字符串D 的值应满足L ≤ D ≤ U,并额外规定错误应具有当前舍入方向的正确符号。

但是,该段是标题为“推荐做法”的小节的一部分。 (如果附件 F 有效,则需要推荐的做法。见 F.5。)

“正确舍入”由当前舍入方向定义。见fsetround

【讨论】:

@kamal:希望对您有所帮助。

以上是关于C中的printf如何对浮点数进行舍入?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Irvine32 WriteFloat 而非 printf 显示舍入为 .001 的浮点数

C/C++如何对浮点数取余?(如果需要的话)

在 Matlab 中对浮点数进行位修改

C语言编写函数,对浮点数保留两位小数,对第三位四舍五入。程序如下 #include<stdio.h

如何在shell中对浮点数进行计算

对浮点数进行位移