JS浮点数值问题10bet:

来源:http://www.chinese-glasses.com 作者:Web前端 人气:113 发布时间:2020-04-15
摘要:时间: 2019-10-01阅读: 208标签: 数值一、JS能够正确表示的整数 姚丽冰      学号:16050120089 Math.pow(2,53) //即9007199254740996"9007199254740996".length //169007199254740993 // 9007199254740992 链接: https://j

时间: 2019-10-01阅读: 208标签: 数值一、JS能够正确表示的整数

姚丽冰      学号:16050120089

Math.pow(2,53) //即9007199254740996"9007199254740996".length //169007199254740993 // 9007199254740992

链接:https://juejin.im/entry/59f9ed38f265da430b7a9090

备注:

【嵌牛导读】:众所周知,JavaScript 浮点数运算时经常遇到会 0.000000001 和 0.999999999 这样奇怪的结果,如 0.1+0.2=0.30000000000000004、1-0.9=0.09999999999999998,很多人知道这是浮点数误差问题,但具体原因就说不清楚了。

  1. 2的53次方,并不是按数值长度设置的,2. 总数量级为百万亿3. 超出会显示,但是不正常(表现为max+1=max;输入16~21之间位数的9会直接往上+1等;)所以不能用于正常计算4.21位会显示1e+21

【嵌牛鼻子】:浮点数,javascript,大数危机,四则运算

安全值判断(ES6)Number.isSafeInteger(num):

【嵌牛提问】:这背后的原理以及解决方案是什么,JS中的大数危机是什么,四则运算中会遇到哪些坑。

Number.isSafeInteger(9007199254740993) // false (存在一个边界问题)Number.isSafeInteger(990) // trueNumber.isSafeInteger(9007199254740993 - 990) // true9007199254740993 - 990// 返回结果 9007199254740002// 正确答案应该是 9007199254740003

【嵌牛正文】:

二、浮点型数

浮点数的存储

9007199254700.3333 //9007199254700.334"9007199254700.334".length //17(包含小数点).000001 //0.000001.0000001 //1e-71.0000001 //1.00000019.0071992547409919 //9.007199254740993

首先要搞清楚 JavaScript 如何存储小数。和其它语言如 Java 和 Python 不同,JavaScript 中所有数字包括整数和小数都只有一种类型 — Number。它的实现遵循 IEEE 754 标准,使用 64 位固定长度来表示,也就是标准的 double 双精度浮点数(相关的还有float 32位单精度)。计算机组成原理中有过详细介绍,如果你不记得也没关系。

备注:

注:大多数语言中的小数默认都是遵循 IEEE 754 的 float 浮点数,包括 Java、Ruby、Python,本文中的浮点数问题同样存在。

  1. 并不是两边都是2的53次方而是合在一起2. 一起共16位,超出向上取整,3. 如果小数位数超出,不一定是截断还是向上进,如上3. 0.000001会显示1e-7,但前面带个整数就不会了

这样的存储结构优点是可以归一化处理整数和小数,节省存储空间。

以上仅表示一些测试现象,留做注意和参考

64位比特又可分为三个部分:

三、位运算(32位)

符号位S:第 1 位是正负数符号位(sign),0代表正数,1代表负数

JS里面尽管能够正确表示的数值量在2^53,但是位运算能够正常运算的范围却依然是32位,第一位为符号位,所以是2^31,转成10位的边界值是(2147483648)。

指数位E:中间的 11 位存储指数(exponent),用来表示次方数

~~21474836472147483647~~2147483648-2147483648~~2147483649-2147483647~~2147483650-2147483646

尾数位M:最后的 52 位是尾数(mantissa),超出的部分自动进一舍零

备注:

10bet 1

  1. JS的位移运算符有:(|,,,,~,^) ,要特别注意使用时候的位数限制

  2. 如上的按位取反运算可以看出,超出部分会变成负数往后减

64 bit allocation

四 、两位小数价格的处理方式

实际数字就可以用以下公式来计算:

一般处理方式都是转整以后运算,再除以100,例如:

数字计算公式

const priceMultiply=(a,b)=(a*b*100+.5|0)/100; //乘法priceMultiply(18.12,10) //181.2 //后面不带0的数字(18.12*10).toFixed(2) //181.20 //后面带0的字符串

10bet 2

备注:这里要注意toFixed()函数的语法:

注意以上的公式遵循科学计数法的规范,在十进制中 0<M<10,到二进制就是 0<M<2。也就是说整数部分只能是1,所以可以被舍去,只保留后面的小数部分。如 4.5 转成二进制就是 100.1,科学计数法表示是 1.001*2^2,舍去1后 M = 001。E是一个无符号整数,因为长度是11位,取值范围是 010bet,~2047。但是科学计数法中的指数是可以为负数的,所以约定减去一个中间数 1023,[0,1022] 表示为负,[1024,2047] 表示为正。如 4.5 的指数 E = 1025,尾数 M = 001。

NumberObject.toFixed(num)

最终的公式变成:

所以写之前需要保证toFixed的对象是数字,否则会报错

10bet 3

//报错情况:(null).toFixed(2)(undefined).toFixed(2)("222.333").toFixed(2)//特殊:NaN是数字,但是无法取小数,也并不会报错(NaN).toFixed(2) //"NaN"

所以 4.5 最终表示为(M=001、E=1025):

10bet 4

4.5 allocation map

(图片由此生成 http://www.binaryconvert.com/convert_double.html)

下面再以 0.1 为例解释浮点误差的原因,0.1 转成二进制表示为 0.0001100110011001100(1100循环),1.100110011001100x2^-4,所以 E=-4+1023=1019;M 舍去首位的1,得到 100110011...。最终就是:

10bet 5

0.1 allocation map

转化成十进制后为 0.100000000000000005551115123126,因此就出现了浮点误差。

为什么 0.1+0.2=0.30000000000000004?

计算步骤为:

// 0.1 和 0.2 都转化成二进制后再进行运算

0.00011001100110011001100110011001100110011001100110011010 +

0.0011001100110011001100110011001100110011001100110011010 =

0.0100110011001100110011001100110011001100110011001100111

// 转成十进制正好是 0.30000000000000004

为什么 x=0.1 能得到 0.1?

恭喜你到了看山不是山的境界。因为 mantissa 固定长度是 52 位,再加上省略的一位,最多可以表示的数是 2^53=9007199254740992,对应科学计数尾数是 9.007199254740992,这也是 JS 最多能表示的精度。它的长度是 16,所以可以近似使用 toPrecision(16) 来做精度运算,超过的精度会自动做凑整处理。于是就有:

0.10000000000000000555.toPrecision(16)

// 返回 0.1000000000000000,去掉末尾的零后正好为 0.1

// 但你看到的 `0.1` 实际上并不是 `0.1`。不信你可用更高的精度试试:

0.1.toPrecision(21) = 0.100000000000000005551

大数危机

可能你已经隐约感觉到了,如果整数大于 9007199254740992 会出现什么情况呢?

由于 E 最大值是 1023,所以最大可以表示的整数是 2^1024 - 1,这就是能表示的最大整数。但你并不能这样计算这个数字,因为从 2^1024 开始就变成了 Infinity

> Math.pow(2, 1023)

8.98846567431158e+307

> Math.pow(2, 1024)

Infinity

本文由10bet发布于Web前端,转载请注明出处:JS浮点数值问题10bet:

关键词:

最火资讯