zoukankan      html  css  js  c++  java
  • 0.1 + 0.2 不等于 0.3 的原理以及解决方案的原理描述

    问题描述

    console.log(0.1 + 0.2);
    
    //0.30000000000000004
    

    原理

    浮点数在计算机中的表示是用阶码与尾数结合的形式。

    0.75 的二进制表现模式就是(1 + 1 * 2 ^ -1) * 2 ^ -1,为了看得直观,这里表达式中的2没有用10来表示

    二进制中2 ^ -1表示的其实是十进制中的0.5。想想十进制中幂次方多1代表数值10倍,那么二进制中幂次方多1代表数值2倍。

    0.75 在二进制中是可以妥善表现出来的。

    var num = 0.75;
    num.toString(2);
    // 0.11
    

    而0.1在二进制中却是一个表现不出来的无限不循环数,所以只能取一个近似数。

    而计算机精度有限,所能表现的值而非真正的0.1,0.2,所以自然相加时有偏差

    解决方案

    0.1 + 0.2 // 0.30000000000000004
    (0.1 + 0.2).toFixed(2) // "0.30"
    parseFloat((0.1 + 0.2).toFixed(2)) // 0.3
    
    24.99 * 10000 // 249899.99999999997
    parseFloat((24.99 * 10000).toFixed(2)); // 249900
    

    But why ? see es6文档 20.1.3.3 Number.prototype.toFixed ( fractionDigits )

    toFixed 设计标准介绍

    那么问题来了,为何toFixed能够解决精度问题,而非简单的省却小数点做法,也非纯字符串操作并处理进位问题。

    翻开es6文档 20.1.3.3 Number.prototype.toFixed ( fractionDigits ) 有答案

    注意最为关键的一步

    Number.prototype.toFixed ( fractionDigits )
    
    1. Let x be thisNumberValue(this value).
    2. ReturnIfAbrupt(x).
    3. Let f be ToInteger(fractionDigits). (If fractionDigits is undefined, this step produces the value 0).
    ...
    7. Let s be the empty String.
    8. If x < 0, then
        a. Let s be "-".
    ...
    10. Else x < 10^21,
        a. Let n be an integer for which the exact mathematical value of n ÷ 10f – x is as close to zero as possible. If there are two such n, pick the larger n.
        b. If n = 0, let m be the String "0". Otherwise, let m be the String consisting of the digits of the decimal representation of n
    ...
    
    11. Return the concatenation of the Strings s and m
    

    代入 (0.1 + 0.2).toFixed(2) // "0.30"

    1. 令 x = 0.1 + 0.2 = 0.30000000000000004
    3. 令 f = 2,代表保留两位小数
    ...
    7. 设 s 为空字符串
    8. x 不小于 0,这里的 s 还是空字符串
    ...
    10. x < 10^21
        a. 设 n,n / 10^2 - 0.30000000000000004  尽可能为0,n 约等于 30.0000000000000004,取30。
        b. 若 n 为 0,设 m "0",否则 m 为 "30"
        c. (解释起来很麻烦,总之这里其实是补0操作,最后得到的 m 为 0.30)
    
    ...
    
    11. 返回的 s 为空字符串, m 是 "0.30",所以结果就是 0.30
    

    另外注意标准里 fractionDigits 小于 0 或大于 20 时会抛出 RangeError 错误,而在 Chrome 62 版浏览器中笔者亲测小于 0 或大于 100 才会抛出

    (0.1 + 0.2).toFixed(101)
    VM586:1 Uncaught RangeError: toFixed() digits argument must be between 0 and 100
        at Number.toFixed (<anonymous>)
        at <anonymous>:1:13
    
  • 相关阅读:
    几个前端可能会遇到的小问题
    函数内部变量与该函数名冲突会怎样
    顺序表之删除算法
    顺序表之插入算法
    IPV4和IPV6的区别
    win10关闭自动更新
    spring常见十大异常
    java中list和Arrylist的区别
    垃圾收集器与内存分配策略
    java类加载机制
  • 原文地址:https://www.cnblogs.com/everlose/p/12497855.html
Copyright © 2011-2022 走看看