整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

javascript中如何解决精度丢失问题

javascript中如何解决精度丢失问题

项目开发中,你一定遇到过精度丢失的问题!比如某个无良的后端返回了一个超16位的数字订单号,比如0.1+0.2 !=0.3等等:

let a=0.1235678912345623456
console.log(a)
let b=256.1235678912345623456
console.log(b)
let c=0.1
let d=0.2
console.log(c + d)
let e=2.55
let f=1.55
console.log(e.toFixed(1))
console.log(f.toFixed(1))



01

精度丢失的原因

是什么原因造成的捏?

嗯?

大道理咱也不会说,问就是一句话回复:

在十进制转二进制的过程中,因js存储位数有限制,末位就会0舍1入取近似值,从而导致再转回十进制时产生误差。



02

如何解决


  • 使用toPrecision;
  • 乘以10的N次幂转换成整数,再除以N次幂转回小数;
  • 先加上1e-14或Number.EPSILON再取值;
  • 使用第三方库,处理大数的 bignumber.js,处理小数的number-precision 和 decimal.js,处理JSON序列化json-bigint。

使用toPrecision


这个方法主要用于纯展示。先用toPrecision保留一定位数的经度,再通过parseFloat展示。

例如:

console.log(parseFloat(0.30000000000000004.toPrecision(12)))

这个12是精度默认值或者说是一个经验值,12位一般都能解决0000...1和0000...9的问题。一般不适合浮点数的运算。


先转整数进行运算,再转成小数


这种方式是我之前浮点运算时经常用到的方法。我们拿0.1+0.2的运算来举例:

let a=(0.1*10 + 0.2*10) / 10
console.log(a) //0.3

但这种方法也不是万能的,有时候也会失精:

console.log(33.41*100) //3340.9999999999995
console.log(2.55*100)  //254.99999999999997

真是防不胜防啊!这时候我们也可以使用Math.round()方法来达到我们的目的,如下:

console.log(Math.round(33.41*100))  //3341
console.log(Math.round(2.55*100))  //255
console.log((Math.round(0.1*10) + Math.round(0.2*10)) / 10)  //0.3

先加上1e-14或Number.EPSILON再取值


给浮点数加上一个极小的数就可以达到目的。获取极小的数有两种方式:

  • 1e-14(10的-14次方)
  • ES6的极小的常量Number.EPSILON


先来看看Number.EPSILON是什么?

console.log(Number.EPSILON)  //2.220446049250313e-16
console.log(Number.EPSILON.toFixed(20))  //0.00000000000000022204

这两种方式都可以,看自己的喜好啦!

let a=0.1235678912345623456
console.log((a+1e-14).toFixed(1)) //0.1
console.log((0.1 + Number.EPSILON + 0.2 + Number.EPSILON).toFixed(1))
//0.3

第三方库

第三方库主要介绍以下四种:

  • bignumber.js的使用-处理大数

安装:

npm install --save bignumber.js

引入:

import BigNumber from 'bignumber.js';

使用:

const a=new BigNumber(0.2);
const b=new BigNumber(0.1)
let c=a.plus(b)
console.log(c) //BigNumber {s: 1, e: -1, c: Array(1)}
console.log(c.toString())  //0.3
console.log(c.toNumber())  //0.3

官网:https://mikemcl.github.io/bignumber.js/


  • number-precision的使用-处理小数

安装:

npm install number-precision --save

引入:

import NP from 'number-precision';

使用:

console.log(NP.strip(0.09999999999999998)); // 四舍五入:=0.1
console.log(NP.round(0.105, 2)) //取2位小数,四舍五入:=0.11
console.log(NP.plus(2.3, 2.6)); // 加:=4.9
console.log(NP.minus(1.0, 0.9)); // 减:=0.1
console.log(NP.times(3, 0.3)); // 乘:=0.9
console.log(NP.divide(0.9, 0.3)); // 除:=3
console.log(NP.plus(0.1, 0.2))


  • number-precision的使用-处理小数

安装:

npm install --save decimal.js

引入:

import Decimal from "decimal.js"

使用:

console.log(Decimal(0.1).add(Decimal(0.2))); // 加: {s: 1, e: -1, d: Array(1), constructor: ?}
console.log(Decimal(0.1).sub(Decimal(0.2))); // 减:{s: -1, e: -1, d: Array(1), constructor: ?}
console.log(Decimal(0.1).mul(Decimal(0.2))); // 乘:{s: 1, e: -2, d: Array(1), constructor: ?}
console.log(Decimal(0.1).div(Decimal(0.2))); // 除:{s: 1, e: -1, d: Array(1), constructor: ?}

上面的结果是Decimal 对象,取值需要Number 或 String转换

官网:http://mikemcl.github.io/decimal.js/


  • json-bigint的使用-处理后端的返回值

安装:

npm install json-bigint

引入:

import JSONBigInt from 'json-bigint';
const JSONBigIntNative=JSONBigInt();

使用:

let json="{\"order\":258431607934229718,\"price\":1.258431607934229718,\"no\":123456}"
const obj1=JSON.parse(json)
const obj2=JSONBigIntNative.parse(json)
console.log(obj1) //{order: 258431607934229730, price: 1.2584316079342297, no: 123456}
console.log(obj2.order.toString())  //258431607934229718
console.log(obj2.price.toString())  //1.258431607934229718
console.log(obj2.order.toNumber())  //258431607934229730
console.log(obj2.price.toNumber())  //1.2584316079342297

主要是把大数据转成字符串。其实关于这一点,如果后端经验丰富的话,自然会给前端返回字符串,也无需前端转换。不过呢,可能前端的技能的提升也就是从无经验的后端开始的噢!

oundation 可以很简单的创建一个提醒框:

提醒框可以使用 .alert-box 类创建, 可以添加可选的类: .secondary, .success, .info, .warning.alert:

实例

<div data-alert class="alert-box">

This is a default alert box.

</div>

<div data-alert class="alert-box secondary">

This is a secondary alert box.

</div>

<div data-alert class="alert-box success">

<strong>Success!</strong> This alert box indicates a successful or positive action.

</div>

<div data-alert class="alert-box info">

<strong>Info!</strong> This alert box indicates a neutral informative change or action.

</div>

<div data-alert class="alert-box warning">

<strong>Warning!</strong> This alert box indicates a warning that might need attention.

</div>

<div data-alert class="alert-box alert">

<strong>Alert!</strong> This alert box indicates a dangerous or potentially negative action.

</div>


圆角提醒框

.radius.round 类用于为提醒框添加圆角:

实例

<div data-alert class="alert-box success radius">

<strong>Success!</strong> Alert box with a radius.

</div>

<div data-alert class="alert-box info round">

<strong>Info!</strong> Alert box that is rounded.

</div>


关闭提醒框

要关闭提醒框,可以在连接或按钮元素上添加 class="close" 类,并初始化 Foundation JS:

实例

<div data-alert class="alert-box">

This is a default alert box with closing functionality.

<a href="#" class="close">&times;</a>

</div>

<script>

// Initialize Foundation JS For Functionality

$(document).ready(function() {

$(document).foundation();

})

</script>

提醒框的宽度为容器的 100%。

&times; (×) 是一个 HTML 字符实体表示一个关闭按钮的图标,而不是字母 "x"。

/面包理想

一转眼已经2018年,前端行业也风风雨雨地走过了10多年,网页布局也从最原始的文档变成了精彩纷呈的交互。当我看到第四代CSS布局技术的时候,在惊叹互联网发展如此突飞猛进的同时,不禁会有一个疑问:CSS经历1.0到3.0的版本变迁,最终又将走向哪里?

今天我们就回顾一下CSS简史和四次布局技术的跃迁。

1.CSS简史

为什么我们需要回顾一下CSS简史呢?

1.了解过去能够更好地预测未来,毕竟太阳底下没有什么新鲜事。

2.相比预测未来,通过了解CSS发展演变趋势,能够科学合理地评判CSS的发展,指导我们学习CSS的核心技术,让我们在有限的精力和时间内学对知识,学好知识。

那是1989年的第一场雪,比1988年来的更早一些,伯纳斯·李(Tim Berners-Lee)以超人的智慧和消耗了前额无数浓密的头发为代价发明了World Wide Web,没有他就没有我们今天互联网相关的工作,也就没有了这个专栏教程,请允许我代表广大前端致以崇高的敬意。我们先一睹大神的风采。

对互联网之父,我只想对他说一句话,有一款洗发水增发效果挺好的,我一直用,你要不要试试?

互联网诞生了以后,最初的网页仅仅是纯文本,但是随着互联网的发展,大家意识到web的原始版本根本就没有提供一种装饰网页的方法。这就好比一个婴儿不会穿着衣服出生一样,孩子大了,总不能裸奔吧?这个时候两个大神提供了解决方案Pei Yaun Wei和Andreesen。

Pei Yaun Wei说,这个好办,我们可以给孩子穿上纸尿裤。

Pei-Yuan Wei在1991年创建图形浏览器 ViolaWWW ,他整合了自己提出的样式语言到自己开发的浏览器中,还期望自己的样式语法最终能成为web关于样式的官方标准。虽然这个目标并未达到,但是他提出的样式语法确实为其它的一些样式语法提供了一些灵感。

Andreesen说,那玩意得换多麻烦,我给孩子画一身衣服吧,当然你懂的,最后Pei Yaun Wei的方案被采用了,但是我们还是看看Andreessen画出来的情况有多乱。

与此同时,Andreessen 在他开发的网景浏览器中进行了不同的尝试。他并没有创建一种分离式的标记语言,而是采取拓展HTML标签的方法来包含非标准化的HTML标签已达到装饰网页的目的。不幸的是,没过多久,网页就失去了所有的语义化并看起来像下面这样混乱:

<MULTICOL COLS="3" GUTTER="25">
 <P><FONT SIZE="4" COLOR="RED">This would be some font broken up into columns</FONT></P></MULTICOL>

最终被大家采纳的语言是由Hakon Wium 在 1994年 10月提出的样式语法。它被称为样式层叠表,简称CSS,但是直到1996年的时候,CSS才演变成我们熟悉的样子。

html { margin-left: 2cm; font-family: "Times", serif;
}h1 { font-size: 24px;
}

然后在1998年5月W3C发表了CSS2,紧接着一个让我们深恶痛觉的浏览器诞生了!对没错,就是你深恶痛绝的那个万能的IE6,2001年微软发布了IE6,不过搞笑的是,IE6最初的出现确实很大程度推动了CSS发展。那时候的网页已经变成跟现在很接近了。

按照常理你肯定会想,后面我就知道了,你不用说了,然后就是CSS3.0了。好吧,如果是我,我也这样想,但是国际友人的脑回路可能跟我们不一样,事实上,CSS3早于1999年已经开始制订,直到2011年6月7日,CSS 3 Color Module终于发布为W3C Recommendation。这个故事告诉我们两件事:

1.w3c这个组织活的真够长,甚至比很多读者年龄都大。

2.不是CSS正式版发布了你才放心使用,如果等到那个时候使用,你的项目可能未曾绽放就枯萎了。

细心的读者可能会问了,CSS3正式版什么时候发布啊?另外CSS4.0什么时候发布?

好吧,我只能告诉你随缘吧……而且没有CSS4.0了,也不会有CSS5.0了。

来我们再看看国际友人的脑回路:

简单地说,就是从CSS3开始,CSS规范就被拆成众多模块(module)单独进行升级,或者将新需求作为一个新模块来立项并进行标准化。因此今后不会再有CSS4、CSS5这种所谓大版本号的变更,有的只是CSS某个模块级别的跃迁。

按照CSS工作组的说法,CSS历史上并没有版本的概念,有的只是“级别”(level)的概念。比如,CSS3其实是CSS Level 3,CSS2是CSS Level 2,而CSS Level 1当然就是CSS1。每个级别都以上一个级别为基础。

大家可能说这个命名好乱啊,这事儿我只想跟你说,你就把CSS工作组当成你女朋友就好了,她开心就好,她说的都是对的,她说啥就是啥……

至于我们,会用就好了。

CSS出现的好处就是让结构和表现分离,可以更灵活的修饰网页,学习也很简单。这里我更想说说它的不足。

1.CSS只有一个全局的命名空间,所以是无法避免出现选择器冲突的。

2.模块化做的不够好,所以造成嵌套和覆盖混乱,容易产生一大堆乱糟糟的样式。

所以现在CSS也在向“模块化、JS化发展”

不过客观地说,CSS的出现确实是互联网里程碑式的进步。

CSS其实就做了两件事:

1.如何布局

2.元素怎么表现

说直白一点就是两件事,一个房子是盖成两室一厅还是三室两厅,另一件事是精装修。

这里大家就会看到如果一个房子180平米隔成1个10平米的主卧170平米的厕所,你再怎么精装修也不会是一个宜居的房间。所以布局在CSS中是极其重要的。与CSS发展简史类似,CSS布局也经历了一代又一代的迭代,才成为当前的样子。

接下来我们就说说CSS布局简史。

2.CSS布局简史

初代table布局

在1997年的时候,David Siegel 改变了web,他自己研究出了一项网页布局技术,利用html中的table元素和gif图片缝合在一起,创造了表格布局技术,之后他就犹如一头猛兽泛滥起来。

优点:布局容易、快捷、兼容性好

缺点:改动不便,需要重新调整,工作量大

由于互联网网站越来越复杂,内容和业务更新频繁,所以table布局是完全不能胜任的,以至于table布局的发明人都说:

“我把炸酱和面倒在了一起,并且没法分开它。”

不过这个真不是我杜撰出来的,原文:

David Siegel:“有人说我毁掉了Web,我回答他们,的确如此。我毁掉了Web是因为我把巧克力和花生酱混合在一起却再也不能把它们分开。我犯下了把结构跟表现混合在一起的错误。”

然后第二代布局技术登场了,

CSS+div布局

CSS+div布局总结起来有三大优点,

1.省时,学习容易,写代码也很容易,很快,效率高

2.省事,如果业务逻辑变了,改起来特别方便快捷

3.省钱,代码量少,省带宽,适合seo

基于这三年不难看出CSS+div布局人畜无害,人见人爱,也就不难解释为什么被广泛地使用成为目前主流的布局技术了。当然了他也有缺点:

1.需要考虑平台兼容性,对制作人员的技能要求较高。

2.在移动端布局显得有些力不从心,如未知宽高float内部元素居中、垂直水平布局、响应式布局等方面略显繁琐。

基于此,第三代布局技术应运而生。

Flex布局

优点:

1.CSS3的布局方式,可以在不使用其他框架的情况下,简便、完整、响应式地实现各种页面布局 2.移动端布局简直太友好

缺点:

兼容性较差,IE浏览器版本在9.0以上,基本要10.0 对于其他浏览器,要求兼容性写法

Flex布局日渐成为移动端主流布局技术,但是它是单一维度的布局,这个我会在专栏后面讲到,有时候也会捉襟见肘,所以目前出现了第四代的布局技术:

grid布局

因为目前大部分浏览器并不支持这种技术,但是它代表了网络发展,这里大家保持关注就好,这里我重点说下它为什么可以称为第四代网络布局技术。首先一个观点大家先记住:

它并不会取代第三代的布局技术,而是颠覆和突破。就好像Flex远比div+CSS布局更方便,但是div+CSS依然有用武之地。

我们说下它的突破之处:

1.flex对标的是float,本质上还是一维布局,这就跟别人开着夏利,你开奔驰都是地面上跑没啥本质区别一样。但是grid升维了,grid是飞机,在地面马路这条线一维之上让人能够思考高度这个维度,以前是汽车一维交通工具(你只能在水平方向一个方向开),飞机是二维(能俯冲了(横向、纵向同时)),所以grid可以说是拓宽了CSS布局的维度。不排除将来会有三维布局的出现。到时候CSS不仅仅能控制横向布局,纵向布局,还可以深度布局(这个要依赖于三维展示的出现,如VR,AR三维立体的展示设备出现)。

2.grid布局里面采用了“可视化布局(template部分,所见即所得)”,这个是颠覆了传统的,写一句代码刷一下浏览器这样的开发方式,不排除以后会出现代码即效果的开发模式。比如你在一个设备上画一个区域,然后画轮播图。

这种方式类似于vc++控件方式,但是更智能,也更友好。谁说不可能呢?大家不要忘了grid布局的来源是早就废弃的table布局。说到这里我多说一句搞笑的微软,frontpage没火,dreamwaver火了,最早提出“canvas”概念的 VML没火,最后html5的canvas火了,连CSS3网格布局也是由微软创建的一个模块 ,最后火起来居然没人认识它。心疼微软一秒钟。

说到这里大家对CSS简史和布局也有所了解了,我们总结下,通过本文你应该学到:

(1)CSS的发展历史。

(2)熟知布局的发展历史,以便对未来布局技术的发展有一个客观的判断,来选择是否学习。

通过本文的学习大家已经对CSS相关基础知识有所了解,接下来我们就开始真正的进入技术的学习,大家是不是迫不及待了呢?