JavaScript – Decimal
前言
之前就写过一篇 decimal, double, float, 但有点杂乱, 这篇把 JS 的部分独立写成一篇整理版.
参考
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的更多相关文章
- javascript 技巧总结积累(正在积累中)
1.文本框焦点问题 onBlur:当失去输入焦点后产生该事件 onFocus:当输入获得焦点后,产生该文件 Onchange:当文字值改变时,产生该事件 Onselect:当文字加亮后,产生该文件 & ...
- 玩转JavaScript OOP[0]——基础类型
前言 long long ago,大家普遍地认为JavaScript就是做一些网页特效的.处理一些事件的.我身边有一些老顽固的.NET程序员仍然停留在这种认知上,他们觉得没有后端开发肯定是构建不了系统 ...
- JSLint检测Javascript语法规范
前端javascript代码编写中,有一个不错的工具叫JSLint,可以检查代码规范化,压缩JS,CSS等,但是他的语法规范检查个人觉得太“苛刻”了,会提示各种各样的问题修改建议,有时候提示的信息我们 ...
- Jint .net平台的javascript引擎
使用需求 有时候一段Javascript代码写的很棒,而我们又无法将之翻译成.net或翻译之成本很高的时候 我们就可以使用Jint引擎来运行Javascript代码,来得到我们想要的结果 或者上 ht ...
- 9月12日JavaScript脚本语言
JS脚本语言 JS脚本语言全称JavaScript,是网页里面使用的脚本语言,也是一门非常强大的语言. 一.基础语法 1.注释语法 单行注释:// 多行注释:/**/ 2.输出语法 ①alert(信息 ...
- mvc 数据验证金钱格式decimal格式验证
mvc 数据验证金钱格式decimal格式验证 首先看下代码 /// <summary> /// 产品单价 /// </summary> [Display(Name = &qu ...
- JavaScript读二进制文件并用ajax传输二进制流
综合网上多个教程,加上自己实践得出的方法,目前能够兼容谷歌.IE11.IE10. htmlbody里的内容,没什么特殊的. <div id="dConfirm"> &l ...
- [转载]JavaScript 中小数和大整数的精度丢失
标题: JavaScript 中小数和大整数的精度丢失作者: Demon链接: http://demon.tw/copy-paste/javascript-precision.html版权: 本博客的 ...
- javascript: jquery.gomap-1.3.3.js
from:http://www.pittss.lv/jquery/gomap/solutions.php jquery.gomap-1.3.3.js: /** * jQuery goMap * * @ ...
- JavaScript正则表达式小记
RegExp.html div.oembedall-githubrepos{border:1px solid #DDD;border-radius:4px;list-style-type:none;m ...
随机推荐
- 用这开源小书学 Docker,香!
> 最新.全面.通俗.可多端阅读的 Docker 教程小书.>> 编程导航开源仓库:https://github.com/liyupi/code-navDocker 可以说是一个改变 ...
- npm ERR! `perfix` is not a valid npm option
全局路径cmd命令:npm config set perfix "D:\Program Files\nodejs\node_global" 缓存路径cmd命令:npm config ...
- oeasy教您玩转vim - 14 - # 行头行尾
行头行尾 回忆上节课内容 我们这次了解了 大词 和 小词 小词 就是我们常规意义的词 被 =." 等标点分开的词 大词 里面包括了 =." 等标点 只能被空格.tab.换行分割 W ...
- 如何免费提取PDF里的图片-pdfimages使用教程
写在前面 本随笔是非常菜的菜鸡写的.如有问题请及时提出. 可以联系:1160712160@qq.com GitHhub:https://github.com/WindDevil (目前啥也没有 动机 ...
- 如何正确使用@Bulider与<T>返回数据
@Data @ToString @Builder @AllArgsConstructor public class PageResult<T> implements Serializabl ...
- docker nginx容器的均衡负载
创建三个docker容器以实现nginx的负载均衡 编写nginx的dockfile [root@docker nginx]# cat Dockerfile FROM nginx RUN echo ' ...
- Jmeter函数助手2-Random
Random函数用于获取随机范围内的正整数或负整数. 一个范围内的最小值:必填,且必须填入整数(正负数都可以) 一个范围内允许的最大值:必填,且必须填入整数(正负数都可以).最大值需大于最小值如[-6 ...
- Cesium 实现可视域分析
*前言:尝试了网上好多个版本的可视域分析,感觉都有一些问题,我这个也可能不是最完美的,但是我觉得对我来说够用了,实现效果如下* 此示例基于vue3上实现,cesium版本1.101.0 ,vite-p ...
- ComfyUI插件:ComfyUI layer style 节点(三)
前言: 学习ComfyUI是一场持久战,而ComfyUI layer style 是一组专为图片设计制作且集成了Photoshop功能的强大节点.该节点几乎将PhotoShop的全部功能迁移到Comf ...
- 使用pycharm专业版(支持远程调试及运行)如何运行mpi的代码呢???(mpi4py的代码)
问题如题: 请注意:这里pycharm专业版的远程调试及运行该如何设置不进行介绍. 由于mpi进程启动是需要执行mpiexec或mpirun命令的,然而在pycharm中我们只能远程调用Python命 ...