PHP 在使用 int 强制转换过程中发生的数据丢失
看以下这段代码
php
<?php
$value = 646.80 * 100;
$valueForceInt = (int) $value;
// $valueForceInt 的值是 64679数据转换
这里使用二进制和 IEEE 754 标准解释这个问题
646.80 这个十进制数由两部分组成:整数部分 646 和小数部分 0.80 。
整数部分转换(646 → 二进制)
646 ÷ 2 = 323 ... 0
323 ÷ 2 = 161 ... 1
161 ÷ 2 = 80 ... 1
80 ÷ 2 = 40 ... 0
40 ÷ 2 = 20 ... 0
20 ÷ 2 = 10 ... 0
10 ÷ 2 = 5 ... 0
5 ÷ 2 = 2 ... 1
2 ÷ 2 = 1 ... 0
1 ÷ 2 = 0 ... 1结果 : 646₁₀ = 1010000110₂
小数部分转换(0.80 → 二进制)
0.80 × 2 = 1.6 ... 整数部分 1,小数部分 0.6
0.6 × 2 = 1.2 ... 整数部分 1,小数部分 0.2
0.2 × 2 = 0.4 ... 整数部分 0,小数部分 0.4
0.4 × 2 = 0.8 ... 整数部分 0,小数部分 0.8
0.8 × 2 = 1.6 ... 循环开始结果 : 0.80₁₀ = 0.1100110011...₂ (无限循环小数)
IEEE 754双精度浮点数表示
在64位系统中,PHP的 float 遵循IEEE 754双精度标准:
- 符号位 :1位
- 指数位 :11位(偏移量1023)
- 尾数位 :52位(隐藏位1)
646.80 的二进制表示
合并整数和小数部分 :
1010000110.1100110011...₂尾数部分 :取小数点后的52位(不足补零):
010000110110011001100110011001100110011001实际存储的二进制值(近似值)
符号位 指数位 尾数部分(52位)
0 10000001000 010000110110011001100110011001100110011001乘法运算的精度损失
当执行 646.80 * 100 时:
- 十进制计算 :
646.80 × 100 = 64680 - 二进制计算 :由于
646.80存储的是近似值,乘法结果也是近似值
假设 646.80 实际存储值略小于真实值(如 646.799999999999943157 ):
646.799999999999943157 × 100 = 64679.9999999999943157强制类型转换的截断行为
PHP的 (int) 转换直接截断小数部分:
64679.9999999999943157→64679(而非四舍五入的64680)
验证代码
php
$value = 646.80 * 100;
var_dump($value); // float(64679.999999999)
var_dump((int)$value); // int(64679)为什么 646.80 * 100 不等于 64680 ?
- 十进制 :
646.80 × 100 = 64680(精确) - 二进制 :由于
646.80存储为近似值,乘法结果也是近似值64679.999999999...
这就是浮点数运算在二进制层面的精度丢失问题
