a * b 使用 float 算术。 100 * 1.05 使用 double 算术。
您的 C 实现可能使用 IEEE-754 binary64 表示 double 和 binary32 表示 float。
1.05 是一个 double 常量。源文本中的十进制数字 1.05 转换为最接近的可表示 double 值,即 4728779608739021•2-52 = 1.0500000000000000444089209850062616169452667236328125。
float b = 1.05; 将该值转换为float。转换的结果是最接近的可表示float 值,即 4728779393990656•2-52 = 4404019•2-22 = 1.0499999523162841796875。
当b 在float 算术中乘以100 时,结果是最接近实数算术结果的可表示float 值。它是 13762559•2-17 = 104.99999237060546875。
当打印小数点后六位时,结果为“104.999992”。
在100 * 1.05中,仅使用double算术;没有float 值。当double 1.05000000000000000444089209850062616169452667236328125 乘以 100 时,结果是最接近实数运算结果的 double。是105•20 = 105。当打印小数点后六位时,结果当然是“105.000000”。
总结:浮点运算一般存在舍入误差。在float 算术中,当打印多个数字时,它们很容易被看到。在这个使用 double 算术的示例中,舍入误差恰好抵消了——最接近 1.05 的 double 略高于 1.05,因此转换中的舍入向上舍入。然后,当它乘以 100 时,最接近实数运算结果的 double 略低于它,因此乘法中的舍入向下舍入。这些有效地取消了,产生了 105 个。
如果执行printf("%f\n", 100*1.05f);,1.05f 将直接转换为float 而不是double,并且乘法将使用float 算术,1 和您将看到与 a * b 相同的结果。
脚注
1 C 标准允许使用额外的精度,但通常您会在这里看到相同的结果。