IEEE754 单精度值(例如您的float
)只有大约七位十进制精度。除此之外,它是不精确的。您甚至不必将其乘以 1000,将其乘以 10 即可得到42930568
.
您可以看到以下代码发生了什么:
#include <iostream>
int main () {
float xyzzy = 42930570;
std::cout.precision (5);
std::cout << "xyzzy is " << std::fixed <<xyzzy << '\n';
return 0;
}
输出不那么精确的:
42930568.00000
为了更全面地解释,IEEE754 浮点值的精度受到限制,仅仅是因为它们具有有限数量的可用于该目的的位。单精度值的长度为 32 位,其中 23 位用于小数(其他位用于符号和指数)。这 23 位相当于大约 7 个十进制数字。你可以在这里找到进一步的分析。
数字 42930570 无法以单个精度值精确表示。您要么得到 is 的位模式0x4c23c462
,要么得到42930568
下一个更高0x4c23c463
的42930572
.
他们被转换为float
而不是的uint64_t
原因是因为这就是标准所说的。在 C++03 中,“乘法运算符”部分 (5.6) 说:
通常的算术转换是在操作数上执行的,并确定结果的类型。
通常的算术转换在第 5 节第 9 段中有详细说明,包括:
- 如果任一操作数是 long double 类型,则另一个应转换为 long double。
- 否则,如果任一操作数为双精度,则另一个应转换为双精度。
- 否则,如果任一操作数为浮点数,则另一个应转换为浮点数。
- 更多处理整数类型的东西(实际上并没有这么说,我在解释)。
由于您有 afloat
和 a uint64_t
,因此上面的第三个要点涵盖了这一点。在“浮点-积分转换”部分(4.9)中,我们看到:
整数类型或枚举类型的右值可以转换为浮点类型的右值。如果可能,结果是准确的。否则,它是下一个较低或较高可表示值的实现定义选择。
因此,这就是为什么您会看到精度下降的原因。
这在 C++11 中没有改变。措辞发生了变化,并且更加冗长,但这些部分仍然归结为相同的结果。