【问题标题】:Rounding doubles - .5 - sprintf舍入双打 - .5 - sprintf
【发布时间】:2009-06-15 06:45:30
【问题描述】:

我使用以下代码舍入到 2dp:

sprintf(temp,"%.2f",coef[i]); //coef[i] returns a double

成功将 6.666 舍入到 6.67,但舍入时无法正常工作 5.555。它返回 5.55,而它应该(至少在我看来)返回 5.56。

当下一个数字是 5 时,我怎样才能让它四舍五入?即返回 5.56。

编辑:我现在意识到这种情况正在发生,因为当我用 cin 输入 5.555 时,它得到 另存为 5.554999997。

我将尝试分两个阶段进行舍入 - 首先是 3dp,然后是 2dp。任何其他 (更优雅的)想法?

【问题讨论】:

    标签: c++ rounding printf


    【解决方案1】:

    看来您必须使用数学舍入函数才能正确舍入。

    printf("%.2f %.2f\n", 5.555, round(5.555 * 100.)/100.);
    

    这在我的机器上给出了以下输出:

    5.55 5.56
    

    【讨论】:

    • 这似乎比添加 0.0005 的想法更可靠,但我猜两者都可以。感谢您的帮助!
    • 这不是错误的,@ypnos,你只是还没有理解浮点数的限制。 5.5551 表示为 5.555100000000000370... 将向上取整,5.555 表示为 5.5549999999... 将向下取整。您可以通过尝试使用 5.5500000000000000001 的代码来看到这是一个表示问题 - 在这种情况下,两个输出都是不正确的 5.55,因为存储时的数字不是 5.55000...1。这是 5.549999...
    • 我很好地理解了浮点的限制。我在这里看到的错误是差异,而您可能会争辩说它是预期的错误(由于格式的限制)。并感谢您对正确答案投反对票!
    • @ypnos 我没有给你投反对票,我的风格不是对“竞争”的答案这样做——我宁愿让更大的社区来决定。无论如何,您的回答完全没有问题,只是您之后发表的评论 - 这不是错误。 IEE754 的局限性得到了很好的理解和记录(除了似乎仍然在 SO 上提出问题的成群结队的人之外的所有人 :-)。如果实际行为与记录的行为不匹配,这只是一个错误;这不是这里的情况。
    【解决方案2】:

    数字 5.555 在 IEEE754 中不能表示为精确数字。用"%.50f" 打印出常量5.555 结果:

    5.55499999999999971578290569595992565155029300000000
    

    所以它向下舍入。尝试改用这个:

    printf ("%.2f\n",x+0.0005);
    

    虽然您需要小心可以精确表示的数字,因为它们会被这个表达式错误地四舍五入。

    您需要了解浮点表示的局限性。如果获得准确性很重要,您可以使用(或编码)BCD 或其他没有 IEEE754 表示法缺点的十进制类。

    【讨论】:

    • 这不会影响您的回答,但我只是好奇:您使用什么语言打印 5.554999999999999971578290569595992565155029300000000?它实际上应该是 5.55499999999999971578290569595992565155029296875000
    • @Rick,很可能是 CygWin 下的 gcc,但很难记住一年前的情况 :-) 我在 Ubuntu10 下得到的结果与你相同。但我认为双精度无论如何只能保证 15 位小数的准确性,因此它可能使用更广泛的内部格式(我有一个模糊的回忆,至少一个系统上的计算在内部使用 80 位,但那是很久以前的事了 - 得到 48与您的数字一样的十进制数字,您需要大约 160 位的精度)。
    • 好的,谢谢。我知道它不可能是 Visual C++(它不能准确地打印那么多数字),而且对于 Linux 上的 gcc/glibc 来说它的数字似乎太少了(它可以让你全部打印出来)。在任何情况下,您都可以使用标准双精度(53 位)获得那么多数字——它是双精度的精确十进制表示(即使它可能只是分配给它的 15 位近似值)。
    【解决方案3】:

    另一个可能的解决方案如何:

    printf("%.2f", _nextafter(n, n*2));
    

    这个想法是将数字从零(n*2 得到正确的符号)增加浮点数学可表示的最小可能量。

    例如:

    double n=5.555;
    printf("%.2f\n", n);
    printf("%.2f\n", _nextafter(n, n*2));
    printf("%.20f\n", n);
    printf("%.20f\n", _nextafter(n, n*2));
    

    使用 MSVC 产生:

    5.55
    5.56
    5.55499999999999970000
    5.55500000000000060000
    

    【讨论】:

      【解决方案4】:

      这个问题被标记为 C++,所以我将在这个假设下继续。请注意,与 C printf 系列不同,C++ 流将四舍五入。您所要做的就是提供您想要的精度,流库将为您四舍五入。我只是把它扔在那里,以防你还没有理由不使用流。

      【讨论】:

        【解决方案5】:

        您也可以这样做(节省乘法/除法):

        printf("%.2f\n", coef[i] + 0.00049999);
        

        【讨论】:

        • 这会将 5.549999 舍入到 5.56 ;-)
        • @ypnos,你真的需要在发帖前检查一下——它根本不会给你 5.56。
        • 抱歉误解。你还是应该明白我的意思。 5.544999 + 0.0005 舍入为 5.55。它应该清楚地四舍五入到 5.54。不需要火箭科学就能意识到,如果不引入新的偏差,添加偏差不会神秘地解决舍入问题。
        • 少数几个我会反对的答案之一。这是完全错误的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-11-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-19
        • 1970-01-01
        相关资源
        最近更新 更多