doule小数的发票金额*100转成分后,取余出现结果少了一分钱。
场景:发票金额为保留2位小数点的double类型8945.63,用它*100后,取余计算,结果确是894562。比预想结果小一分钱。
后经排查原因:php取余计算%,是先将被除数取整进行计算,此处取余过程中以将double转int的过程中已经出错。
要搞明白这个原因, 首先我们要知道浮点数的表示(IEEE 754): 浮点数, 以64位的长度(双精度)为例, 会采用1位符号位(E), 11指数位(Q), 52位尾数(M)表示(一共64位).
符号位:最高位表示数据的正负,0表示正数,1表示负数。
指数位:表示数据以2为底的幂,指数采用偏移码表示
尾数:表示数据小数点后的有效数字.
以0.58,0.57为例:
-
0.58的二进制表示基本上(52位)是: 0010100011110101110000101000111101011100001010001111
-
0.57的二进制表示基本上(52位)是: 0010001111010111000010100011110101110000101000111101
-
而两者的二进制, 如果只是通过这52位计算的话,分别是:
-
0.58 -> 0.57999999999999996
-
0.57 -> 0.56999999999999995
-
0.58 * 100 = 57.999999999
-
那你intval一下, 自然就是57了....
找到原因就要解决,php中两数相乘可以用bcmul这个函数,来避免精度丢失。
$remainder = intval(bcmul($invoice_amount, 100));
问题解决。特记录一下,以备后查