php中double乘法计算少一分钱

764 阅读2分钟

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为例:

  1. 0.58的二进制表示基本上(52位)是: 0010100011110101110000101000111101011100001010001111

  2. 0.57的二进制表示基本上(52位)是: 0010001111010111000010100011110101110000101000111101

  3. 而两者的二进制, 如果只是通过这52位计算的话,分别是:

  4. 0.58 -> 0.57999999999999996

  5. 0.57 -> 0.56999999999999995

  6. 0.58 * 100 = 57.999999999

  7. 那你intval一下, 自然就是57了....

找到原因就要解决,php中两数相乘可以用bcmul这个函数,来避免精度丢失。

$remainder = intval(bcmul($invoice_amount, 100));

问题解决。特记录一下,以备后查