printf对双精度浮点数的舍入行为

16

有人能解释一下这种行为吗?我非常了解浮点数的机器级表示。这似乎与printf及其格式有关。 两个数字都是通过浮点数表示精确的(检查:乘以64得到一个整数)。

#include <stdio.h>
#include <iostream>
using namespace std;

int main() {
  double x1=108.765625;
  printf("%34.30f\n", x1);
  printf("%9.5f\n", x1);
  printf("%34.30f\n", x1*64);

  double x2=108.046875;
  printf("%34.30lf\n", x2);
  printf("%9.5f\n", x2);
  printf("%34.30f\n", x2*64);
}

输出:

> 108.765625000000000000000000000000
> 108.76562
> 6961.000000000000000000000000000000
> 108.046875000000000000000000000000
> 108.04688
> 6915.000000000000000000000000000000

注意,第一个数字会向下舍入,第二个数字会向上舍入。


1
你可能会对我的文章感兴趣:http://www.exploringbinary.com/inconsistent-rounding-of-printed-floating-point-numbers/。一些实现使用“向远离零的方向舍入”而不是“向最近的偶数舍入”。 - Rick Regan
1
看起来微软在VS2010和VS2015之间改变了默认的四舍五入行为。我刚从一个版本升级到另一个版本,遇到了一些非常恼人且微妙的错误。这篇博客可能是为了突出这个变化,但你完全可以忽略它。 - omatai
2个回答

20

这是“四舍五入到偶数”或“银行家舍入”。如果一个数字恰好处于两个数字中间,那么被舍入的表示中的最后一位数字将选择偶数。

http://linuxgazette.net/144/misc/lg/a_question_of_rounding_in_issue_143.html:
对于GNU C库,printf()使用的舍入规则是“银行家舍入”或“四舍五入到偶数”。这比其他一些C库更正确,因为C99规范指出,转换为十进制应该使用当前选择的IEEE舍入模式(默认为银行家舍入)。


2
链接文章的作者是不称职的。“二进制和十进制基数不共享同一组无理数”-这是无稽之谈。有理/无理与基数无关。 - R.. GitHub STOP HELPING ICE
1
@R..确实。但这篇文章所引用的段落是正确的,正如作者在他的缺陷报告中痛苦地学到的那样。 - Daniel Fischer
并非所有有限小数展开都具有有限的二进制展开。 - Antti Haapala -- Слава Україні

0

%9.5f 输出的是小数点后保留 5 位数字,且该数字是最接近源数字的。


问题的关键在于:如果源数字恰好处于两个可能输出之间,该怎么办? - Spike0xff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接