事情的起因
要从今天的微信支付调试说起,众所周知微信支付的接口,要求传入的金额数值是按分为单位的,所以都必须是大于0的整数。 所以我们一般业务的实际金额都要做乘以100的处理,例如某大妈刚在市场买了1.10元的菜,她摸了摸口袋,看了一眼诚实憨厚的大叔,已经麻利地帮她打包好菜,实在不忍心说出自己今天忘记带钱包。 大叔正打算把菜递给大妈之际,看到大妈尾头紧皱,已经失去平时要把1.10元的菜讲价到1.00元的风韵神采,便意识到事情并不简单,便默默从下方抽屉中抽出一个微信二维码递给她。 大妈心中暗喜,迅速拔出他儿子刚为她买的最新款红米手机,熟练地扫过二维码,也没有要讲价到1.00元的意思了,麻利地在微信上输入1.10元的金额,点击立即支付,输入过密码。。。 “参数错误:你输入的金额格式不正确”,看着手机弹出的提示弹窗,大妈狠狠咬了牙,又重复了一遍上述的支付动作,“参数错误:你输入的金额格式不正确”。这时大妈崩溃的用着恳求可怜的眼神看着大叔,大叔也一面无奈地轻轻将打包好的菜微微收了过来一下,空气突然安静地凝固起来。
问题剖释
空中传来旁白君的声音,它解析道: 没错,这为大妈所使用的程序,其实就是某知名博文《1.10乘100为什么不等于110》的博主还未调试完之前写的代码。 他就是这样把金额1.10乘100然后传过去微信的支付接口,他是那样的信心满满,因为他之前含着泪用自己的微信零钱支付的测试都是那样一条条地测试成功。他一次次发起1.10元支付测试,看着一次次弹出的反馈信息“参数错误:你输入的金额格式不正确”,他越觉得莫名奇妙,1.10乘100不就等于110吗,还有什么格式错误的?然而当这个too young too naive的少年,找出这个输入结果后,空气又突然安静地凝固起来。
代码语言:javascript复制>1.10*100
110.00000000000001
WHAT THE *!
IEEE754 双精度浮点数
Javascript 作为一门动态语言,其数字类型只有 number 一种。 nubmer 类型使用的就是美国电气电子工程师学会 IEEE754 标准中以64-bit存储的的双精度浮点。 而浮点数表示方式具有以下特点: 1.浮点数可表示的值范围比同等位数的整数表示方式的值范围要大得多;
2.浮点数无法精确表示其值范围内的所有数值,而有符号和无符号整数则是精确表示其值范围内的每个数值;
3.浮点数只能精确表示m*2e的数值;
4.当biased-exponent为2e-1-1时,浮点数能精确表示该范围内的各整数值;
5.当biased-exponent不为2e-1-1时,浮点数不能精确表示该范围内的各整数值。
由于部分数值无法精确表示(存储),于是在运算统计后偏差会愈见明显。
So Why 1.10 * 100 === 110.00000000000001?
1.1.10实际存储时的位模式是: 0011111111110001100110011001100110011001100110011001100110011001; 2.100实际存储时的位模式是 0110010000000000000000000000000000000000000000000000000000000000; 3.实际存储的位模式作为操作数进行浮点数乘法: 110000110011100010111011110111100110100001100000000000000。 转换为十进制即为110.00000000000001。
0.1 0.2
其实这和那著名的 0.1 0.2 === 0.30000000000000004问题是一个道理的,可笑的是,只要我们的大妈把价讲到1.00元
代码语言:javascript复制>1.00*100
100
就不会有问题了。
总结
JS坑常有,我们慢慢填之。