zoukankan      html  css  js  c++  java
  • js 浮点数计算精度不准确问题

    或许很多人都遇到过,js 对小数的加、减、乘、除时经常得到一些奇怪的结果!

    比如 :0.1 + 0.2 = 0.3  ?

    这么一个简单的计算,当你用js 计算时会发现结果是:0.30000000000000004 。这么奇葩,简直无法理解!

    那,为什么会这样呢?

    对于浮点数的四则运算,几乎所有的编程语言都会有类似精度误差的问题,只是 C++ / C# / Java 这些语言中已经封装好了方法来避免精度的问题,而javascript 是一门弱类型的语言,从设计思想上就没有对浮点数有个严格的数据类型,所以精度误差的问题就显得格外突出。

    那为什么 0.1 + 0.2 这种简单的运算,计算机还搞不定呢?那是因为,计算机无论做什么运算,其实都是转成二进制来运算的,因为它只能读懂二进制,而不是十进制,所以我们先把0.1 和 0.2转换成二进制看看:

    0.1 => 0.0001 1001 1001 1001... (无限循环)

    0.2 => 0.0011 0011 0011 0011... (无限循环)

    我们发现0.1和0.2转化为二进制之后,变成了一个无限循环的数字,这在现实生活中,无限循环我们可以理解,但计算机是不允许无限循环的,对于无限循环的小数计算机会进行舍入处理。进行双精度浮点数的小数部分最多支持52位,所以两者相加之后得到这么一串0.0100110011001100110011001100110011001100110011001100 因浮点数小数位的限制而截断的二进制数字,这时候,我们再把它转换为十进制,就成了 0.30000000000000004。

    找到原因了,那应该怎么处理呢?

    方法一:指定要保留的小数位数(0.1+0.2).toFixed(1) = 0.3;这个方法toFixed是进行四舍五入的也不是很精准,对于计算金额这种严谨的问题,不推荐使用,而且不通浏览器对toFixed的计算结果也存在差异。

    方法二:把需要计算的数字升级(乘以10的n次幂)成计算机能够精确识别的整数,等计算完毕再降级(除以10的n次幂),这是大部分编程语言处理精度差异的通用方法。 

    eg: (0.1*10 + 0.2*10) / 10 == 0.3

    对于方法二可以对四则运算分别作封装:

    /* ===== 浮点型数据的加、减、乘、除 ===== */
        add(arg1, arg2) { // 加法
            let r1, r2, m
            try {
                r1 = arg1.toString().split('.')[1].length
            } catch (e) {
                r1 = 0
            }
            try {
                r2 = arg2.toString().split('.')[1].length
            } catch (e) {
                r2 = 0
            }
            m = Math.pow(10, Math.max(r1, r2))
            return (arg1 * m + arg2 * m) / m
        },
        sub(arg1, arg2) { // 减法
            let r1, r2, m, n
            try {
                r1 = arg1.toString().split('.')[1].length
            } catch (e) {
                r1 = 0
            }
            try {
                r2 = arg2.toString().split('.')[1].length
            } catch (e) {
                r2 = 0
            }
            m = Math.pow(10, Math.max(r1, r2))
            n = (r1 >= r2) ? r1 : r2
            return ((arg1 * m - arg2 * m) / m).toFixed(n)
        },
        mul(arg1, arg2) { // 乘法
            let m = 0
            let s1 = arg1.toString()
            let s2 = arg2.toString()
            try {
                m += s1.split('.')[1].length
            } catch (e) {
            }
            try {
                m += s2.split('.')[1].length
            } catch (e) {
            }
            return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, m)
        },
        div(arg1, arg2) { // 除法
            let t1 = 0
            let t2 = 0
            let r1
            let r2
            try {
                t1 = arg1.toString().split('.')[1].length
            } catch (e) {
            }
            try {
                t2 = arg2.toString().split('.')[1].length
            } catch (e) {
            }
            r1 = Number(arg1.toString().replace('.', ''))
            r2 = Number(arg2.toString().replace('.', ''))
            return (r1 / r2) * Math.pow(10, t2 - t1)
        },
        /* ===== 浮点型数据的加、减、乘、除 ===== */
  • 相关阅读:
    编程范式 epesode7,8 stack存放指针类型and heap,register
    编程范式 episode 6 实现stack 栈功能_ to do
    C 运算符优先级
    编程范式 episode3 and 4,5
    编程范式 epesode2 negative values, float 精度
    C 数据类型 长度
    memcpy code
    排序算法 2 qsort 库函数,泛型函数
    sin, miss the mark, correct our aim and try again
    排序算法 1
  • 原文地址:https://www.cnblogs.com/garfieldzhong/p/8359754.html
Copyright © 2011-2022 走看看