C 为什么printf(“%.3f\n”123456.987654)打印123456.984?

C 为什么printf(“%.3f\n”123456.987654)打印123456.984?,c,floating-point,printf,double,precision,C,Floating Point,Printf,Double,Precision,为什么下面的printf打印的是123456.984而不是123456.988 #include <stdio.h> int main() { printf("%.3f\n", 123456.987654); return 0; } 该错误与浮点精度密切相关。它可以用float常量复制 使用浮点常量执行此printf(),将显示您描述的行为。但是,如果传递一个双常量,则结果与预期一致: #include <stdio.h> int main() {

为什么下面的printf打印的是123456.984而不是123456.988

#include <stdio.h>
int main()
{
    printf("%.3f\n", 123456.987654);
    return 0;
}

该错误与浮点精度密切相关。它可以用
float
常量复制

使用浮点常量执行此
printf()
,将显示您描述的行为。但是,如果传递一个双常量,则结果与预期一致:

#include <stdio.h>
int main()
{
    printf("%.3f\n", 123456.987654f);  // float: insufficient precision .984
    printf("%.3f\n", 123456.987654);   // double: better precision .988
                    // unsuffixed floating point constants are double
    return 0;
}
#包括
int main()
{
printf(“%.3f\n”,123456.987654f);//浮点:精度不足。984
printf(“%.3f\n”,123456.987654);//双精度:更高的精度。988
//非固定浮点常量是双精度的
返回0;
}

这是由于内部表示的近似,即使用2的幂和2的幂的分数来表示数字。转换回十进制时与数字对应的最接近的表示形式

这种奇怪的舍入行为依赖于实现:C标准没有指定必须使用哪种浮点表示


您使用的编译器肯定是基于标准的。在上,您可以验证如何使用IEEE以单精度对所选浮点数进行编码:对于123456.987654,几乎使用了全部范围的位,并且可以以单精度表示的最接近的数字是123456.984375

在我的电脑上,下面的程序(用gcc编译)打印出来

123456.988
123456.988
123456.984
您的问题中的代码实际上是什么显示了问题,还是涉及到了浮点?如果有一个浮点数,那么问题是一个浮点数只有7位数的精度。当打印到小数点后3位时,与数字最接近的浮点数约为123456.984

#include    <stdio.h>
#include    <stdlib.h>

    int main( void)
    {
        printf("%.3f\n", 123456.987654);
        double  d = 123456.987654;
        printf("%.3f\n", d);
        float   f = 123456.987654;
        printf("%.3f\n", f);
        return EXIT_SUCCESS;
    }
#包括
#包括
内部主(空)
{
printf(“%.3f\n”,123456.987654);
双d=123456.987654;
printf(“%.3f\n”,d);
浮点数f=123456.987654;
printf(“%.3f\n”,f);
返回退出成功;
}

您遇到的问题似乎与实现/编译器有关(甚至可能与编译或执行有关)。这对我来说是不可复制的

对于gcc 9.1版和clang 9.0版,在线Godbolt编译器和执行器的输出都是
123456.988


以下是。

也许
123456.984
123456.987654
的最接近(四舍五入)的近似值?有关详细信息,请参见例如。@Someprogrammerdude:不,
123456.987654
是一个
double
,C标准要求
double
具有足够的精度,以便生成“123456.988”。(
DBL_DIG
必须至少为10,因此任何10位重要的十进制数字都可以四舍五入到
double
,然后再返回,不做任何更改。)问题中当前显示的代码在macOS 10.14.6上使用Apple Clang 11.0.0输出“123456.988”,我预计在其他常见的C实现中也会这样。您使用的编译器、编译器版本、编译器标志/开关和操作系统是什么?要确保这是生成所述输出的代码,请删除可执行文件,尝试执行它以确保它已消失,然后从头开始重建。@EricPostpischil在前面的问题的注释中,OP stated s s/he正在Windows下使用code::Blocks with MingW gcc。这可能是她/他的设置。在OP没有进一步信息和编辑的情况下,应将其关闭为不可复制。问题中显示的代码不会产生在正确的C实现中声明的输出。OP可能有打印
float
对象或常量的代码,这可以解释输出,但目前提出的问题似乎不正确。关于“二的幂和二的幂分数”:我想你的意思是“二的幂,包括负整数指数的幂。”@EricPostischil确实没有d,因为默认情况下它是d。之所以使用它,是因为编译器允许它,并且它将焦点放在这个问题上。是的,对于科学家来说,二的力量包括整个范围的积极和消极。但并不是每个人都对数学这么满意,所以我更喜欢用更明确的措辞:——)@EricPostPhischil我删除了d,并在评论中澄清了它,使它更明确。并提供了一个很好的参考,以帮助分析IEEE754的单精度编码。@RobertSsupportsMonicaCellio根据C标准%f和%f处理双参数:“表示浮点数的双参数以[-]样式转换为十进制表示法ddd.ddd,其中小数点字符后的位数等于精度规范。”(N2176,第7.21.6节)。-调用printf()时,仍然会执行双重提升。因此,问题发生在双重提升之前,即在提供的浮点值中,因为随后提供的浮点值被转换为双重形式。我认为OP put作为常量存在于浮点变量中。如果你给常数加上一个f后缀,你就可以复制它:有趣的是,float-clang发出了关于double的提示promotion@Christophe是的,类似的事情是可能的,你的答案很好,但一般来说,我们会照原样去做。如果OP真的没有显示原始的发行代码,这是不好的,因为我们需要一个完整的MRE。-但我不认为OP真的以这种方式把事情搞砸了。我将继续对你的问题发表评论。
#include    <stdio.h>
#include    <stdlib.h>

    int main( void)
    {
        printf("%.3f\n", 123456.987654);
        double  d = 123456.987654;
        printf("%.3f\n", d);
        float   f = 123456.987654;
        printf("%.3f\n", f);
        return EXIT_SUCCESS;
    }