C float 初始化结果出乎意料

C float initialization result is unexpected

Visual Studio 2019 社区 float 变量不是我将其初始化的。 为了避免截断,我让它不太精确。 这不是"controllable"吗?

试图以其他方式强制 init 舍入

int main()
{
    // where did my 6 go?
    float b = 80.123456F;
    printf("b:[%f]\n", b);

    // rounds up
    float c = 80.1234569F;
    printf("c:[%f]\n", c);

    // rounds up
    float d = 80.1234561F;
    printf("d:[%f]\n", d);


    // appends a 1
    float e = 80.12345F;
    printf("e:[%f]\n", e);

    // prints what's initialized
    float f = 80.123451F;
    printf("f:[%f]\n", f);

    // prints what's initialized
    float g = 80.123459F;
    printf("g:[%f]\n", g);
}
b:[80.123459]
c:[80.123459]
d:[80.123459]
e:[80.123451]
f:[80.123451]
g:[80.123459]

我从调试器开始,写这个是为了满足 SO 要求。 在我的例子中,调试器匹配了 prints

浮点数最多只能精确到 7 位有效数字。不是 7 十进制 位。 80.123456F 有 8 位有效数字,所以这行不通。

虽然 80.12345 只有 7 位有效数字,但它实际上最终在 80.1234512329101562 左右。只要您不尝试打印比它更多的数字,就可以了。不幸的是,%f 总是在小数点后打印 6 位数字。在这种情况下,这是有道理的。使用 %g 会做得更好,您可以在其中指定有效数字的数量。

两期:

  • printf%f 转换说明符会自行对浮点值进行舍入以供显示。您可以使用精度说明符控制小数点后显示的位数:printf( "%.8f\n", b ); 将打印小数点后最多 8 位数字,而 printf( "%.16f\n", a); 将打印 16 位。不要相信显示的价值,虽然,因为...

  • 无限数量的实数值无法放入有限数量的位中,因此大多数实数值无法精确表示 - 您得到的近似值仅好到这么多小数位。单精度 float 类型不能保证超过 6 到 7 个小数位的精度(这不仅仅是小数点后的位数,而是 总位数 ). double 给你大约 10 到 11 位数字。如果您需要用多位精度(超过 12 位左右)表示值,那么您需要超越本机浮点类型并使用第三方多精度库。

这个问题的解决方法可能是改变数据类型。尽管 double 的运算方式与 float 相同,通过有限的有理数集逼近大范围的实数,但双精度数非常接近,舍入误差不太可能影响。

例如,最接近实数80.123456的float值为80.12345123291015625和80.1234588623046875。 double 最接近 80.123456 的值是 80.123456000000004451067070476710796356201171875 和 80.1234559999999902402123552747070789337158203125.[=17]

float 确实有它的用途,在你处理非常大的精度非常低的数字集的情况下,但 double 是更普遍有用的类型。使用double将物理量取入程序时,测量误差会超过舍入误差。