前言

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

参考

JavaScript 浮点数运算的精度问题

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

Rounding

The Question: 0.1 + 0.2 = ?

JS 有一道经典的问题

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

第一次接触 JS 的人可能会感到不可思议,但其实上面这道题,并不是 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 --dev

使用

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 自己累加实现.

toPrecision() 类似 JS 的 toFixed 返回 string

round() 类似 Math.round 但它支持 round to n decimal point。而且有不同的 rounding mode,默认是四舍五入。

JS Workaround (Number.EPSILON)

如果不想大费周章搞 decimal, 可以用一些小技巧解决.

const value = 0.1 + 0.2;
if (value === 0.3) {
console.log('yes');
} else {
console.log('no'); // will be no, because it is 0.30000000000000004
}

把 if expression 换成

const value = 0.1 + 0.2;
if (0.3 - value < Number.EPSILON) { // EPSILON is a very very small number 2.220446049250313e-16
console.log('yes'); // will be yes
} else {
console.log('no');
}

Number.EPSILON 是 es6 的新特性.

JavaScript – Decimal的更多相关文章

  1. javascript 技巧总结积累(正在积累中)

    1.文本框焦点问题 onBlur:当失去输入焦点后产生该事件 onFocus:当输入获得焦点后,产生该文件 Onchange:当文字值改变时,产生该事件 Onselect:当文字加亮后,产生该文件 & ...

  2. 玩转JavaScript OOP[0]——基础类型

    前言 long long ago,大家普遍地认为JavaScript就是做一些网页特效的.处理一些事件的.我身边有一些老顽固的.NET程序员仍然停留在这种认知上,他们觉得没有后端开发肯定是构建不了系统 ...

  3. JSLint检测Javascript语法规范

    前端javascript代码编写中,有一个不错的工具叫JSLint,可以检查代码规范化,压缩JS,CSS等,但是他的语法规范检查个人觉得太“苛刻”了,会提示各种各样的问题修改建议,有时候提示的信息我们 ...

  4. Jint .net平台的javascript引擎

    使用需求 有时候一段Javascript代码写的很棒,而我们又无法将之翻译成.net或翻译之成本很高的时候 我们就可以使用Jint引擎来运行Javascript代码,来得到我们想要的结果 或者上 ht ...

  5. 9月12日JavaScript脚本语言

    JS脚本语言 JS脚本语言全称JavaScript,是网页里面使用的脚本语言,也是一门非常强大的语言. 一.基础语法 1.注释语法 单行注释:// 多行注释:/**/ 2.输出语法 ①alert(信息 ...

  6. mvc 数据验证金钱格式decimal格式验证

    mvc 数据验证金钱格式decimal格式验证 首先看下代码 /// <summary> /// 产品单价 /// </summary> [Display(Name = &qu ...

  7. JavaScript读二进制文件并用ajax传输二进制流

    综合网上多个教程,加上自己实践得出的方法,目前能够兼容谷歌.IE11.IE10. htmlbody里的内容,没什么特殊的. <div id="dConfirm"> &l ...

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

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

  9. javascript: jquery.gomap-1.3.3.js

    from:http://www.pittss.lv/jquery/gomap/solutions.php jquery.gomap-1.3.3.js: /** * jQuery goMap * * @ ...

  10. JavaScript正则表达式小记

    RegExp.html div.oembedall-githubrepos{border:1px solid #DDD;border-radius:4px;list-style-type:none;m ...

随机推荐

  1. 如何做好一场NPS调研?

    我们在工作中经常遇到的一个词,那就是"产品NPS调研".当部分项目缺少专业的用研人员时,设计师.产品经理则经常会接受上级的要求,投身于NPS调研工作. 笔者也曾在2022年的某天突 ...

  2. JDBC第一天:JDBC的基础

    第一,JDBC叫java数据库连接技术,是用来实现数据库的增.删.改.查的接口技术. 第二,实现数据库的连接步骤:在这之前需要导包 1,准备四大参数 a,,准备驱动类:driverClassName: ...

  3. python项目位置迁移后,虚拟环境无法使用

    一.虚拟环境无法使用问题 修改虚拟环境目录/pyvenv.cfg文件中的路径 建议以下配置全部替换一下 修改虚拟环境目录/scripts/activate.bat文件中** VIRTUAL_ENV参数 ...

  4. 自动化测试框架开发python selenium excel POM

    自动化测试框架开发python selenium excel POM 本文主要是采用python selenium excel POM 做UI自动化测试 测试框架选用的是unittest 测试框架用的 ...

  5. linux性能资源分析工具

    linux性能资源分析工具 1,top 2,ps 3,uptime 4,mpstat 5,pidstat 6,vmstat 7,iostat 8,netstat 9,lsof 10,sar / nmo ...

  6. 15、Spring之基于xml的声明式事务

    阅读本文前,建议先阅读Spring之基于注解的声明式事务 15.1.环境搭建 创建名为spring_transaction_xml的新module,过程参考13.1节 15.1.1.配置打包方式和依赖 ...

  7. 【Java】Properties 配置信息类

    Properties 配置信息类 Properties 是HashTable的子类,该对象用于处理属性文件 由于属性文件的Key.Value都是字符串类型,所以Properties里的Key和Valu ...

  8. 【RabbitMQ】06 Spring整合RabbitMQ

    首先生产者和消费者需要的Maven依赖是一样的 <?xml version="1.0" encoding="UTF-8"?> <project ...

  9. 【OracleDB】 08 子查询

    什么是子查询? 子查询是一种常用计算机语言SELECT-SQL语言中嵌套查询下层的程序模块. 当一个查询是另一个查询的条件时,称之为子查询. Oracle的子查询语法公式: SELECT select ...

  10. Typora配置自动上传图片到图床

      在多平台发布文章时,如果遇到图片不能导入的问题,推荐使用图床!推荐使用阿里云或腾讯云,免费的不用考虑了! PicGo下载 链接:https://pan.quark.cn/s/2ec95402631 ...