处理精度丢之-如何解决

摘要:
Precision=12){return+parseFloat(num.toPrecision(Precision))}提示其他){constbaseNum=Math.pow(10,digitalLength(num2)))return(times(num1,

通过上篇我们了解到计算机是如何存储浮点数,那精度丢失是在哪产生的?


拿0.1 + 0.2举例:

0.1

转二进制后:0.0 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 01(转化后是以0011无限循环,二进制为满一进一,所以末尾为01)

0.2:

转二进制后:0.0 0110 0110 0110 0110 0110 0110 0110 0110 0110 0110 0110 0110 0110 10

相加:0.0 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 11

工具转换为十进制: 0.30000000000000004

其实在四则运算中,浮点数转换成二进制后如果存在无限循环小数,都会有精度异常的情况

那对于精度异常,该如何解决?

1、展示类:错误数据转正

如:strip(0.30000000000000004) ==> 0.3

function strip(num, precision = 12) {
return +parseFloat(num.toPrecision(precision))
}

tips: 此方法仅适用于页面展示,不适用参与计算。至于precision 为什么是12,很多相关资料说根据经验12基本能解决日常的精度丢失。


2、计算类:相加、减、乘、除丢失精度解决方案

如:0.1 + 35.41

这里存在一个容易会踩的坑,我们知道浮点数的计算解决思路是转换为整数。那是不是就是直接将目标值各自乘以10的n次方转化为整数就可以。

就像35.41, 我们想的是乘以100, 转化为整数应该就阔以了。事实是:35.41 * 100 ==》 3540.9999999999995,尴尬极了,所以我们需要通过转换成字符串格式进行化整。

下面代码通过不借助第三方包解决精度丢失:

// 精确加法
function plus(num1, num2) {
  const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)))
  return (times(num1, baseNum) + times(num2, baseNum)) / baseNum
}
// 精确减法
function minus(num1, num2, ...others) {
  const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)))
  return (times(num1, baseNum) - times(num2, baseNum)) / baseNum
}
// 精确乘法
function times(num1, num2, ...others) {
  const num1Changed = float2Fixed(num1)
  const num2Changed = float2Fixed(num2)
  const baseNum = digitLength(num1) + digitLength(num2)
  const leftValue = num1Changed * num2Changed
  checkBoundary(leftValue)
  return leftValue / Math.pow(10, baseNum)
}
// 精确除法
function divide(num1, num2, ...others) {
  const num1Changed = float2Fixed(num1)
  const num2Changed = float2Fixed(num2)
  checkBoundary(num1Changed)
  checkBoundary(num2Changed)
  return times((num1Changed / num2Changed), Math.pow(10, digitLength(num2) - digitLength(num1)))
}
// 检测数字是否越界,如果越界给出提示
function checkBoundary(num) {
  if (_boundaryCheckingState)
    if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
      console.warn(`${num} 超出数字安全范围(${Number.MAX_SAFE_INTEGER},${Number.MIN_SAFE_INTEGER}),计算结果可能不准确`)
    }
  }
}
// 小数转整数
function float2Fixed(num) {
  num = num || 0
  if (num.toString().indexOf('e') === -1) {
    return Number(num.toString().replace('.', ''))
  }
  const dLen = digitLength(num)
  return dLen > 0 ? strip(num * Math.pow(10, dLen)) : num
}
// 小数点后的字符串长度
function digitLength(num) {
  num = num || 0
  const eSplit = num.toString().split(/[eE]/)
  const len = (eSplit[0].split('.')[1] || '').length - (+(eSplit[1] || 0))
  return len > 0 ? len : 0
}
// 错误数据转正常
function strip(num, precision = 12) {
  return +parseFloat(num.toPrecision(precision))
}

借助第三方引入npm包解决:

1) 如Math.js、BigDecimal.js

2) https://github.com/dt-fe/number-precision (介绍说大小只有1k,实用性强)

大整数

大整数丢失精度的原理和浮点数的原理是一致的。双精度的尾数位为11位,JS中能精准表示的最大整数为Math.pow(2, 53), 超过这个数的大整数将丢失精度。

解决方案:BigDecimal.js提供方法解决,原理是将大整数当做字符串处理,缺点是损耗性能

除了上面说的这些,js中还有一个toFixed方法,也存在丢失精度的问题

tofixed方法

对于浮点数,保留对应的位数我们首先想到的就是tofixed方法,然而某些值却不是我们想象的那样。

如1.335.tofixed(2) ==> 1.33

查阅相关资料,发现tofixed方法采用算法是一种叫什么银行家算法,并不是我们数学中的四舍五入。而且针对不同浏览器具体规则还不一致。你说好好的四舍五入不用,为啥得用这个银行家算法,搞不懂,也没查到对应的相关资料说明。

抱怨归抱怨,遇到问题还是得解决。

解决疑问:

1)原生tofixed方法不对,就需要自己来构造一个我们想要的tofixed

     我写的有漏洞,暂时就不拿出来坑大家了,来日补上~

2)假如项目中已经很多地方都使用tofixed,使用自己构造的方法,带来的是改动范围偏大。所以简洁的改动就只能重写原生方法

Number.prototype.toFixed = function(n) {
  let newNum = 0
  if (!n) {
     newNum = Math.round(this)
  } else {
     newNum = Math.round(this * Math.pow(10, n)) / Math.pow(10, n)
  }
  return newNum + ''
}

免责声明:文章转载自《处理精度丢之-如何解决》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇python多线程下载网页图片并保存至特定目录php检测文件只读、可写、可执行权限下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Object非空判断

类Objects,它由一些静态的实用方法组成,这些方法是null-save(空指针安全的)或null-tolerant(容忍空指针的),那么在它的源码中,对对象为null的值进行了抛出异常操作。 public static T requireNonNull(T obj) :查看指定引用对象不是null。 查看源码发现这里对为null的进行了抛出异常操作:...

RxJava学习(一)

注意:文字和图片转载自抛物线博客 参考:http://gank.io/post/560e15be2dca930e00da1083 RxJava 到底是什么 一个词:异步。 RxJava 在 GitHub 主页上的自我介绍是 "a library for composing asynchronous and event-based programs usin...

Spring框架针对dao层的jdbcTemplate操作crud之query查询数据操作

查询目标是完成3个功能: (1)查询表,返回某一个值。例如查询表中记录的条数,返回一个int类型数据 (2)查询表,返回结果为某一个对象。 (3)查询表,返回结果为某一个泛型的list集合。 一、查询表中记录的条数,返回一个int类型数据的操作方法 使用jdbcTemplate 原理是把加载驱动Class.forName("com.mysql.jdbc.D...

java.net.URISyntaxException的解决办法

近日在用HttpClient访问抓取汇率时,为了省力,直接采用 String url = "http://api.liqwei.com/currency/?exchange=usd|cny&count=1"; HttpClient client = new DefaultHttpClient(); HttpGet httpget = new Htt...

.net 中的async,await理解

理解: 1、async修饰的方法可理解为异步方法(必须要配合await,否则和普通方法无异)2、当async方法执行遇到await,则立即将控制权转移到async方法的调用者3、由调用者决定是否需要等待async方法执行完再继续往下执行4、await会挂起当前方法,即阻塞当前方法继续往下执行,转交控制权给调用者 注意:如果调用一个async方 法,却不使用...

java 调用webservice的各种方法总结

java 调用webservice的各种方法总结(本文转自:http://www.blogjava.net/zjhiphop/archive/2009/04/29/webservice.html) 现在webservice加xml技术已经逐渐成熟,但要真正要用起来还需时日!! 由于毕业设计缘故,我看了很多关于webservice方面的知识,今天和大家一...