本文分为三个部分

  1. JS 数字精度丢失的一些典型问题
  2. JS 数字精度丢失的原因
  3. 解决方案(一个对象+一个函数)

一、JS数字精度丢失的一些典型问题

1. 两个简单的浮点数相加

  1. 0.1 + 0.2 != 0.3 // true

Firebug

这真不是 Firebug 的问题,可以用alert试试 (哈哈开玩笑)。

看看Java的运算结果

再看看Python

2. 大整数运算

  1. 9999999999999999 == 10000000000000001 // ?

Firebug

16位和17位数竟然相等,没天理啊。

又如

  1. var x = 9007199254740992
  2. x + 1 == x // ?

看结果

三观又被颠覆了。

3. toFixed 不会四舍五入(Chrome)

  1. 1.335.toFixed(2) // 1.33

Firebug

线上曾经发生过 Chrome 中价格和其它浏览器不一致,正是因为 toFixed 兼容性问题导致

二、JS 数字丢失精度的原因

计算机的二进制实现和位数限制有些数无法有限表示。就像一些无理数不能有限表示,如 圆周率 3.1415926...,1.3333... 等。JS 遵循 IEEE 754 规范,采用双精度存储(double precision),占用 64 bit。如图

意义

  • 1位用来表示符号位
  • 11位用来表示指数
  • 52位表示尾数

浮点数,比如

  1. 0.1 >> 0.0001 1001 1001 1001…(1001无限循环)
  2. 0.2 >> 0.0011 0011 0011 0011…(0011无限循环)

此时只能模仿十进制进行四舍五入了,但是二进制只有 0 和 1 两个,于是变为 0 舍 1 入。这即是计算机中部分浮点数运算时出现误差,丢失精度的根本原因。

大整数的精度丢失和浮点数本质上是一样的,尾数位最大是 52 位,因此 JS 中能精准表示的最大整数是 Math.pow(2, 53),十进制即 9007199254740992。

大于 9007199254740992 的可能会丢失精度

  1. 9007199254740992 >> 10000000000000...000 // 共计 53 个 0
  2. 9007199254740992 + 1 >> 10000000000000...001 // 中间 52 个 0
  3. 9007199254740992 + 2 >> 10000000000000...010 // 中间 51 个 0

实际上

  1. 9007199254740992 + 1 // 丢失
  2. 9007199254740992 + 2 // 未丢失
  3. 9007199254740992 + 3 // 丢失
  4. 9007199254740992 + 4 // 未丢失

结果如图

以上,可以知道看似有穷的数字, 在计算机的二进制表示里却是无穷的,由于存储位数限制因此存在“舍去”,精度丢失就发生了。

想了解更深入的分析可以看这篇论文(又长又臭):What Every Computer Scientist Should Know About Floating-Point Arithmetic

三、解决方案

对于整数,前端出现问题的几率可能比较低,毕竟很少有业务需要需要用到超大整数,只要运算结果不超过 Math.pow(2, 53) 就不会丢失精度。

对于小数,前端出现问题的几率还是很多的,尤其在一些电商网站涉及到金额等数据。解决方式:把小数放到位整数(乘倍数),再缩小回原来倍数(除倍数)

  1. // 0.1 + 0.2
  2. (0.1*10 + 0.2*10) / 10 == 0.3 // true

  

以下是我写了一个对象,对小数的加减乘除运算丢失精度做了屏蔽。当然转换后的整数依然不能超过 9007199254740992。

  1. /**
  2. * floatObj 包含加减乘除四个方法,能确保浮点数运算不丢失精度
  3. *
  4. * 我们知道计算机编程语言里浮点数计算会存在精度丢失问题(或称舍入误差),其根本原因是二进制和实现位数限制有些数无法有限表示
  5. * 以下是十进制小数对应的二进制表示
  6. * 0.1 >> 0.0001 1001 1001 1001…(1001无限循环)
  7. * 0.2 >> 0.0011 0011 0011 0011…(0011无限循环)
  8. * 计算机里每种数据类型的存储是一个有限宽度,比如 JavaScript 使用 64 位存储数字类型,因此超出的会舍去。舍去的部分就是精度丢失的部分。
  9. *
  10. * ** method **
  11. * add / subtract / multiply /divide
  12. *
  13. * ** explame **
  14. * 0.1 + 0.2 == 0.30000000000000004 (多了 0.00000000000004)
  15. * 0.2 + 0.4 == 0.6000000000000001 (多了 0.0000000000001)
  16. * 19.9 * 100 == 1989.9999999999998 (少了 0.0000000000002)
  17. *
  18. * floatObj.add(0.1, 0.2) >> 0.3
  19. * floatObj.multiply(19.9, 100) >> 1990
  20. *
  21. */
  22. var floatObj = function() {
  23.  
  24. /*
  25. * 判断obj是否为一个整数
  26. */
  27. function isInteger(obj) {
  28. return Math.floor(obj) === obj
  29. }
  30.  
  31. /*
  32. * 将一个浮点数转成整数,返回整数和倍数。如 3.14 >> 314,倍数是 100
  33. * @param floatNum {number} 小数
  34. * @return {object}
  35. * {times:100, num: 314}
  36. */
  37. function toInteger(floatNum) {
  38. var ret = {times: 1, num: 0}
  39. var isNegative = floatNum < 0
  40. if (isInteger(floatNum)) {
  41. ret.num = floatNum
  42. return ret
  43. }
  44. var strfi = floatNum + ''
  45. var dotPos = strfi.indexOf('.')
  46. var len = strfi.substr(dotPos+1).length
  47. var times = Math.pow(10, len)
  48. var intNum = parseInt(Math.abs(floatNum) * times + 0.5, 10)
  49. ret.times = times
  50. if (isNegative) {
  51. intNum = -intNum
  52. }
  53. ret.num = intNum
  54. return ret
  55. }
  56.  
  57. /*
  58. * 核心方法,实现加减乘除运算,确保不丢失精度
  59. * 思路:把小数放大为整数(乘),进行算术运算,再缩小为小数(除)
  60. *
  61. * @param a {number} 运算数1
  62. * @param b {number} 运算数2
  63. * @param digits {number} 精度,保留的小数点数,比如 2, 即保留为两位小数
  64. * @param op {string} 运算类型,有加减乘除(add/subtract/multiply/divide)
  65. *
  66. */
  67. function operation(a, b, digits, op) {
  68. var o1 = toInteger(a)
  69. var o2 = toInteger(b)
  70. var n1 = o1.num
  71. var n2 = o2.num
  72. var t1 = o1.times
  73. var t2 = o2.times
  74. var max = t1 > t2 ? t1 : t2
  75. var result = null
  76. switch (op) {
  77. case 'add':
  78. if (t1 === t2) { // 两个小数位数相同
  79. result = n1 + n2
  80. } else if (t1 > t2) { // o1 小数位 大于 o2
  81. result = n1 + n2 * (t1 / t2)
  82. } else { // o1 小数位 小于 o2
  83. result = n1 * (t2 / t1) + n2
  84. }
  85. return result / max
  86. case 'subtract':
  87. if (t1 === t2) {
  88. result = n1 - n2
  89. } else if (t1 > t2) {
  90. result = n1 - n2 * (t1 / t2)
  91. } else {
  92. result = n1 * (t2 / t1) - n2
  93. }
  94. return result / max
  95. case 'multiply':
  96. result = (n1 * n2) / (t1 * t2)
  97. return result
  98. case 'divide':
  99. result = (n1 / n2) * (t2 / t1)
  100. return result
  101. }
  102. }
  103.  
  104. // 加减乘除的四个接口
  105. function add(a, b, digits) {
  106. return operation(a, b, digits, 'add')
  107. }
  108. function subtract(a, b, digits) {
  109. return operation(a, b, digits, 'subtract')
  110. }
  111. function multiply(a, b, digits) {
  112. return operation(a, b, digits, 'multiply')
  113. }
  114. function divide(a, b, digits) {
  115. return operation(a, b, digits, 'divide')
  116. }
  117.  
  118. // exports
  119. return {
  120. add: add,
  121. subtract: subtract,
  122. multiply: multiply,
  123. divide: divide
  124. }
  125. }();

toFixed的修复如下

  1. // toFixed 修复
  2. function toFixed(num, s) {
  3. var times = Math.pow(10, s)
  4. var des = num * times + 0.5
  5. des = parseInt(des, 10) / times
  6. return des + ''
  7. }

相关:

http://0.30000000000000004.com

http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

相关:

http://mathjs.org/
http://www.ituring.com.cn/article/73940
https://en.wikipedia.org/wiki/IEEE_floating_point
http://modernweb.com/2014/02/17/what-every-javascript-developer-should-know-about-floating-points/
http://tool.oschina.net/hexconvert/
http://0.30000000000000004.com/
http://demon.tw/copy-paste/javascript-precision.html

JavaScript数字精度丢失问题总结的更多相关文章

  1. JavaScript数字精度丢失的一些问题

    本文分为三个部分 JS 数字精度丢失的一些典型问题 JS 数字精度丢失的原因 解决方案(一个对象+一个函数) 一.JS数字精度丢失的一些典型问题 1. 两个简单的浮点数相加 1 0.1 + 0.2 ! ...

  2. JavaScript数字精度上代码。

    /**不能超过 9007199254740992 * floatObj 包含加减乘除四个方法,能确保浮点数运算不丢失精度 * * 我们知道计算机编程语言里浮点数计算会存在精度丢失问题(或称舍入误差), ...

  3. 关于 JavaScript 的 精度丢失 与 近似舍入

    一.背景 最近做 dashborad 图表时,涉及计算小数且四舍五入精确到 N 位.后发现 js 算出来的结果跟我预想的不一样,看来这里面并不简单-- 二.JS 与 精度 1.精度处理 首先明确两点: ...

  4. php导出CSV时,超长数字精度丢失问题与前导0的字符串丢失0的问题解决

    php生成的CSV有时候会遇到两个特殊情况: 1.输出的字段中,含有超长数字(18位的数字)比方身份证:122121197410180016,就算输出时字段加上"",还是会被识别成 ...

  5. gson ajax 数字精度丢失

    ajax传输的json,gson会发生丢失,long > 15的时候会丢失0 解决方案:直接把属性为long的属性自动加上双引号成为js的字符串,这样就不会发生丢失了,ajax自动识别为字符串. ...

  6. js数字精度丢失

    http://www.cnblogs.com/snandy/p/4943138.html

  7. JavaScript数字计算精度丢失的问题和解决方案

    一.JS数字精度丢失的一些典型问题 1. 两个简单的浮点数相加:0.1 + 0.2 != 0.3 // true,下图是firebug的控制台截图: 看看java的计算结果:是不是让你很不能接受 再来 ...

  8. [转载]JavaScript 中小数和大整数的精度丢失

    标题: JavaScript 中小数和大整数的精度丢失作者: Demon链接: http://demon.tw/copy-paste/javascript-precision.html版权: 本博客的 ...

  9. springboot 解决 数字长度过长导致JS精度丢失问题

    问题 在开发过程中,我们的主键字段使用了数字作为主键ID,发现数字精度丢失的问题. 上图红框是后端日志的输出. 在浏览器端F12 看到的结果如上图,数据居然自动变化,这个是数字在浏览器丢失了精度,导致 ...

随机推荐

  1. Android APP压力测试(二)之Monkey信息自动收集脚本

      Android APP压力测试(二) 之Monkey信息自动收集脚本 前言: 上一篇Monkey介绍基本搬抄官方介绍,主要是为了自己查阅方便.本文重点介绍我在进行Monkey时如何自动收集相关信息 ...

  2. 常用查找数据结构及算法(Python实现)

    目录 一.基本概念 二.无序表查找 三.有序表查找 3.1 二分查找(Binary Search) 3.2 插值查找 3.3 斐波那契查找 四.线性索引查找 4.1 稠密索引 4.2 分块索引 4.3 ...

  3. 【原创】Java和C#下String类型中的==和equals的原理与区别

    一.Java下 1.几个例子 public static void main(String[] arge) { String str1 = new String("1234"); ...

  4. win10与ubuntu下演示运行.net core rc2 1.0.0.3002702程序

    随着.net core rc2(1.0.0.002702)发布的同时,我们也来在本地 win10与ubuntu玩一下吧. 先简单说下.net core ,在.net core rc1中用的是dnx 工 ...

  5. 背水一战 Windows 10 (25) - MVVM: 通过 x:Bind 实现 MVVM(不用 Command)

    [源码下载] 背水一战 Windows 10 (25) - MVVM: 通过 x:Bind 实现 MVVM(不用 Command) 作者:webabcd 介绍背水一战 Windows 10 之 MVV ...

  6. [译]Godot系列教程二 - 场景实例化(Instancing)

    场景实例化(Instancing) 原理阐述 创建一个场景并将节点扔到里面对于小项目是适用的,但随着项目不断发展,用到越来越多的节点,整个项目很快就会演化成难以管理的状态. 为了解决这个问题,Godo ...

  7. asp.net mvc项目自定义区域

    前言 直接上干货就是,就不废话了. 使用场景:分离模块,多站点等~~ 一.分离模块 自定义视图引擎,设置视图路径格式 项目结构图 1.Code: 在Global.asax Application_St ...

  8. 20个编写现代 CSS 代码的建议

    明白何谓Margin Collapse 不同于其他很多属性,盒模型中垂直方向上的Margin会在相遇时发生崩塌,也就是说当某个元素的底部Margin与另一个元素的顶部Margin相邻时,只有二者中的较 ...

  9. 转载 CSS3 经典教程系列:CSS3 盒阴影(box-shadow)详解

    目标大纲 文章转载 CSS3 经典教程系列:CSS3 盒阴影(box-shadow)详解 IE中CSS-filter滤镜小知识大全 CSS实现跨浏览器兼容性的盒阴影效果

  10. 【JQ基础】

    $(document).ready(function(){});//用来在DOM加载完成之后执行一系列预先定义好的函数. $(function(){});//$(document).ready(fun ...