• JavaScript – Decimal


    前言

    之前就写过一篇 decimal, double, float, 但有点杂乱, 这篇把 JS 的部分独立写成一篇整理版.

    参考

    JavaScript 浮点数运算的精度问题

    关于JavaScript中计算精度丢失的问题

    Rounding

    The Question: 0.1 + 0.2 = ?

    JS 有一道经典的问题

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

    第一次基础编程语言的人,可能会感到不可思议. 但其实上面这道题, 并不是 JS 独有的.

    C# 也是一样的计算结果

    public class Program
    {
        public static void Main()
        {
            double x = 0.1;
            double y = 0.2;
            double z = x + y; // 0.30000000000000004
        }
    }

    Why 0.30...4 ?

    人做计算是用十进制, 但电脑是用二进制做计算. 

    0.1 + 0.2, 电脑会先把 0.1 转换成二进制. 而这个二进制是个无穷数 0.0001100110011001...(无限). 所以只能保留一部分的精度 (IEEE 754 标准的 64 位双精度浮点数的小数部分最多支持 53 位二进制位).

    最终相加以后再转换成十进制, 结果就精度丢失了. 结果就有偏差.

    The Answer

    上面说了这道题在 C# 也是同样的计算结果, 但为什么往往叫的人都是 jser 呢? 

    因为 C# 有一个 best practice. 但凡可能会让人计算的数, 请使用 decimal.

    public class Program
    {
        public static void Main()
        {
            decimal x = 0.1m;
            decimal y = 0.2m;
            decimal z = x + y; // 0.3
        }
    }

    换成 decimal 计算就正确了. 

    decimal vs double

    decimal 的特色就是准, 慢, 小.

    所以如果不 care 精准度的话, 大部分情况都会使用 double. (比如做游戏啦, 科学啦, 这些场景一般上需要计算的快, 数目又大, 但通常不需要太准)

    算钱则一定是用 decimal 的.

    How it work?

    为什么 decimal 就 ok 了呢? 因为 decimal 内部是用 string 做计算的.

    它不会直接把 0.1 转成二进制, 而是先把 0.1 转成十进制的 1, 1 的二进制就不会无穷了, 也就避开了进度丢失.

    类似 0.1 x 10 = 1. 但这个转换过程并不是 0.1 * 10, 因为如果用计算机乘法, 那还是会把 0.1 先转去二进制, 然后又丢失了.

    这个 * 10 是用 string 的移位完成的. 

    只要确保电脑在计算时, 不要有小数点, 那么在转换二进制后就不会无穷, 计算以后就不会有进度丢失的问题了.

    这就是 decimal 的基本原理, 也是它为什么慢的根本原因.

    JS Decimal

    好样子, 有 decimal 就能破了, 但是...JavaScript 没有 decimal 丫, C#, Java 都有, 但 JavaScript 偏偏没有...

    JS 在设计之初的宗旨就是简单. 所以舍去了很多特性. 

    简单 > 多人学 > 不够用 > 历史包袱 > 出来混, 迟早要还...

    PHP, JS, Vue 都走了同一条路, 能不能顺利过渡就看个人造化了

    当初 ES4 想一次性变革 (像 Angular2), 但后来还是选了渐进式 (Vue way). 就目前来看也是不错的.

    扯远了, 拉回来. JS 没有原生的 decimal 类型 (tc39 proposal), 但是有 library 可以实现 decimal 的效果.

    big.js, bignumber.js, decimal.js 这 3 个库都是同一个作者.

    big.js 最轻, 也是我目前用着的, 三个之间的区别可以看这篇: What is the difference between big.js, bignumber.js and decimal.js? 或它的翻译篇

    big.js 基本用法

    安装
    yarn add big.js
    yarn add @types/big.js

    使用

    import Big from 'big.js';
    console.log(Big(0.1).plus(0.2).toNumber()); // 0.3

    第一步是把 number 变成 Big 对象.

    Big(0.1) 或者 new Big(0.1) 都可以, new 是 optional 的.

    接着就是调用各做 operator 方法. 比如 plus, minus, mul / times, div (加减乘除, 注: mul 和 times 都是乘, alias 而已)

    最后通过 toNumber 把 Big 对象转换成 JS 的 number 类型.

    除了加减乘除, big js 也提供了许多对比方法, ===, >, >=, <, <= 等等. 这样写起来就比较方便了.

    big.js 没有提供, min, max, sum 这些功能, 需要的话得用 reduce 自己累加实现.

  • 相关阅读:
    input File 提交文件上传 无法传递数据的原因
    谈谈运维该掌握那些技能
    虚拟机vmware 下Centos 系统磁盘扩容
    jenkins针对不同的项目组对用户进行权限分配
    Mac Catalina 手残误删系统文件恢复过程
    自动化持续集成
    zabbix 监控之主动配置模式
    nginx对请求后缀或IP访问限制
    elasticsearch7.2.1 报错 Received fatal alert: handshake_failure
    使用nexus创建npm私服
  • 原文地址:https://www.cnblogs.com/keatkeat/p/16242309.html
Copyright © 2020-2023  润新知