应统一用字符串或整型缩放存储小数,避免PHP与Java浮点类型差异导致精度丢失、字节序错位等问题;JSON中须转字符串再解析,二进制协议需显式指定字节序和格式。

PHP保存小数时Java解析失败,大概率是浮点类型不一致
PHP默认用float(IEEE 754双精度)存小数,Java端若用float或BigDecimal按不同规则解析,就会出现精度偏差、字节错位甚至NumberFormatException。这不是传输问题,而是两端对“小数”没约定好物理表示形式。
常见现象包括:
- PHP写
12.34,Java读成12.339999999999999 - JSON里传
"12.34",Java用Float.parseFloat()后值不准 - 二进制直传(如TCP/UDP)时,PHP用
pack('d', 12.34),Java用ByteBuffer.order(ByteOrder.LITTLE_ENDIAN).getDouble()却读出乱值——字节序没对齐
必须统一为定点数,推荐字符串或整型缩放存储
跨语言场景下,float/double天生不可靠。最稳的方式是放弃“小数”这个概念,改用整数表达“最小单位”,再由业务层解释小数点位置。
例如金额统一到“分”:
立即学习“PHP免费学习笔记(深入)”;
- PHP存:
$cents = (int)round(12.34 * 100); // 得 1234,传1234(int)或"1234"(string) - Java收:
BigDecimal.valueOf(cents).divide(BigDecimal.ONE_HUNDRED)
如果必须传小数字符串(如JSON API),PHP用number_format($val, 2, '.', '')强制两位,避免12.340000000000001这类输出;Java端用new BigDecimal(str)构造,绝不用Double.parseDouble()。
pack/unpack与Java二进制互通需显式指定字节序和格式
若走二进制协议(如自定义报文、Redis raw value),PHP的pack()默认按本机字节序,JavaByteBuffer默认大端(BIG_ENDIAN),x86机器PHP通常是小端,直接对接必错。
正确做法:
- PHP侧固定用
pack('G', $val)(大端双精度)或pack('E', $val)(大端单精度) - Java侧对应用
ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN).putDouble(val) - 或者双方都用小端:
pack('g', $val)+ByteBuffer.order(ByteOrder.LITTLE_ENDIAN)
注意:'d'和'f'在pack()中是本机序,不可用于跨语言;'E'/'G'才保证端序明确。
JSON传输时别信json_encode的默认浮点行为
PHPjson_encode()对float会直接输出科学计数或截断位数,比如0.0000001可能变成1e-7,JavaBigDecimal(String)能解析,但Double.parseDouble()会丢失精度。
更危险的是,PHP浮点本身就有表示误差:json_encode(0.1 + 0.2)可能输出0.30000000000000004,Java再读就彻底失真。
稳妥方案:
- 所有需要精确的小数字段,PHP一律转成字符串再进数组:
['amount' => sprintf('%.2f', $money)] - Java端接收为
String,再用new BigDecimal(str.trim()) - 避免在JSON里传原生
float,也别依赖JSON_PRETTY_PRINT之类修饰——它不解决根本问题
真正麻烦的不是怎么写代码,而是团队里有人悄悄在PHP里json_encode(['price' => $row['price']]),又有人在Java里obj.getPrice().doubleValue()——这种隐式浮点链一旦形成,排查时连日志都看不出哪一环开始漂移。











