【问题标题】:C printf float roundingC printf 浮点数舍入
【发布时间】:2015-05-23 06:02:31
【问题描述】:

我必须用C 重新实现printf(3),而不使用任何可以为我进行转换的函数。

我快完成了,我只需要%a,感谢你们,它也快完成了:How %a conversion work in printf statement?

男人说:

双参数被四舍五入并转换为十六进制表示法 在 style[-]0xh.hhhp[+-]d 中,后面的位数 十六进制点字符等于精度规范。

所以我的问题是四舍五入 如何

我找到了:printf rounding behavior for doubles

它解释了 printf 正在使用 banker roundRound half to even 但我不知道如何实现它我已经尝试过:

a_double = round(a_double * pow(10, precision)) / pow(10, precision)

但在1000000 测试中,从0.000001 开始,每次失败时添加0.000001 405201 次,例如0.000011

printf("%.6a", 0.000011) => 0x1.711948p-17
myprintf("%.6a", 0.000011) => 0x1.711947p-17

四舍五入“失败”,我没有得到与真正的 printf 相同的值。

我不认为将double 转换为hexa notation 的算法是错误的,因为精度为13 我绝对没有错误。

所以我只想知道如何我可以像 printfdouble 所做的一样进行舍入。

【问题讨论】:

  • 如果你需要在普通的二进制浮点机器上做这个,"%a"不需要四舍五入
  • 我怀疑这个问题是“从 0.000001 开始,每次添加 0.000001”而不是 myprintf()。 “加0.000001”和myprintf()有什么关系?请记住,0.000001 不是 完全 0.000001 具有二进制浮点数。发布更多代码,展示您是如何生成测试编号的,以及为什么您认为您得到的结果不正确。
  • 我从0.000001开始,每次都加上0.000001,然后我比较官方printf的结果和我的,我的错了,因为我不知道printf是如何四舍五入的.此外,当精度为 >= 13 时,我绝对没有错误,因为没有发生舍入。

标签: c printf double rounding


【解决方案1】:

好的,所以我猜我之前的算法没有完全实现舍入,所以让我们看看如何对结果进行舍入。我也会使用您的示例编号作为我的示例编号。首先,我们发现 0.000011/(2^(-17)) = 0.000011*(2^17) = 1.441792 所以幂是 -17。然后,我们输出“1.”,从 1.441792 中减去 1 并乘以 16,得到 7.068672。我们输出 7,从中减去 7 并乘以 16,得到 1.09875199999999。我们输出 1,从中减去 1 并乘以 16,得到 1.58003199999985。我们输出 1,减去 1 并乘以 16,得到 9.28051199999754。然后输出9,减9,乘以16,结果是4.48819199996069。我们输出4,减4,乘16,结果是7.81107199937105。

现在,我们要输出最后一个字符。现在我们施展魔法。因为 7.81107199937105 比 7 更接近 8,所以我们输出“8”。这个魔法只适用于最后一个角色。对于非最后一个字符,在确定要输出哪个字符时,始终使用整数部分,而根本不使用小数部分。然后,在这之后,我们输出“p-17”,因为功率是-17。

请注意,通常的四舍五入规则说,同样接近 7 和 8 的 7.5 被四舍五入为 8,而不是 7,而 6.5 将四舍五入为 7,而不是 6。但是,如果你想实现舍入一半到甚至你会遇到例如6.5 然后向下舍入到 6,因为 6 是偶数,而 7 不是。我不确定你对银行家回合的发现是否也适用于 %a,你唯一能做的就是测试实现各种四舍五入算法,看看哪个给出的结果与 printf 的真实 %a 相同。不应该那么难,因为不同的舍入算法只是处理一半的方式不同。其余的四舍五入到最接近的数字。

顺便说一句,我在您之前的问题 (How %a conversion work in printf statement?) 中说错了,因为 3.2 具有非舍入表示 1.999999....p+1 和舍入表示 1.99999ap+1 最后一个“ a" 会由于有限的浮点精度而发生。当然,这是由于四舍五入而不是由于浮点精度有限,您现在可能已经意识到了。

【讨论】:

  • 我实现了它,它适用于您的示例0.000011,但不适用于0.000005 我得到0x1.4f8b59p-18 而不是0x1.4f8b58p-18(如果我们不指定精度,则结果为@987654326 @)
  • 对于最后一位数字,结果是:8.5555200017988682,四舍五入为9,这就是为什么我得到9,不知道为什么printf 留在8跨度>
  • 话虽如此,它仍然比我以前的要好得多,现在只有 60572 失败了 1000000
  • 我想知道你为什么得到 0x1.4f8b58p-18。在我的系统上, printf("%.6a\n", 0.000005);打印 0x1.4f8b59p-18。我猜你没有使用Linux?我认为您系统的 printf 存在缺陷。前进的唯一方法是阅读系统的 printf 实现的源代码,如果它不是开源的,则可能不可用。
  • 我使用的是 OS X 10.10.2,非常奇怪,非常感谢您的帮助:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-09
  • 1970-01-01
  • 1970-01-01
  • 2020-06-09
  • 1970-01-01
相关资源
最近更新 更多