参考(抄)的源网站
- 各种各样的其它网站,因为资料整理时,没有保存源页面,已经不记得了
- webkit Double-precision floating point format
- 百度百科-十进制与二进制转换
javascript 中的数字类型number 是64位双精浮点数
The Number is a primitive data type in JavaScript. Number type represents integer, float, hexadecimal, octal or exponential value. First character in a Number type must be an integer value and it must not be enclosed in quotation marks.
Number 是Javascript的原始类型类型。Number类型代表integer(整形),float(浮点),十六进制数,八进制数,二进制数。Number类型的第一个字符必须是整数,并且不能用引号引起来。
64位双精浮点数
Double-precision binary floating-point is a commonly used format on PCs, due to its wider range over single-precision floating point, in spite of its performance and bandwidth cost. It is commonly known simply as double. The IEEE 754 standard specifies a binary64 as having:
双精度二进制浮点数是PC通用的数据格式,因为它的数据取值范围覆盖单精浮点型,尽管它的性能和带宽成本。它被简称为double. IEEE 754标准规定了一个64位二进制数据包含有:
- Sign bit: 1bit
- Exponent: 11 bits
- Significand precision: 53bits(52 explicitly stored) The sign bit determines the sign of the number (including when this number is zero, which is signed).
标志位声明了number数据的标记(包括number是0)0 代表+正数,1代表负数;因为Math.pow(-1,n);
The exponent field is an 11-bit unsigned integer from 0 to 2047, in biased form: an exponent value of 1023 represents the actual zero. Exponents range from −1022 to +1023 because exponents of −1023 (all 0s) and +1024 (all 1s) are reserved for special numbers.
指数字段是一个11位的无符号的整数,它的取值从0-2047,偏移量: 当一个指数值是1023时代表偏移量=0。偏移的取值范围是-1022 ~ +1023(0-1023 ~ 2047-1023),因为-1023(00000000000) 和+1024(1111111111)是被指派到指定的偏移量了;
The 53-bit significand precision gives from 15 to 17 significant decimal digits precision (2−53 ≈ 1.11 × 10−16). If a decimal string with at most 15 significant digits is converted to IEEE 754 double-precision representation, and then converted back to a decimal string with the same number of digits, the final result should match the original string. If an IEEE 754 double-precision number is converted to a decimal string with at least 17 significant digits, and then converted back to double-precision representation, the final result must match the original number.
53位有效位数精度为15到17个有效十进制数字精度(2-53≈1.11×10-16)。 如果将最多具有15个有效数字的十进制字符串转换为IEEE 754双精度表示形式,然后再转换回具有相同位数的十进制字符串,则最终结果应与原始字符串匹配。 如果将IEEE 754双精度数字转换为具有至少17个有效数字的十进制字符串,然后再转换回双精度表示形式,则最终结果必须与原始数字匹配。
关于二进制
二进制转十进制
小数点前或者整数要从右到左用二进制的每个数去乘以2的相应次方并递增,小数点后则是从左往右乘以二的相应负次方并递减。
1101.01(2)=
Math.pow(2,3) + Math.pow(2,2) + Math.pow(2,0) + Math.pow(2,-2) =
8 + 4 + 1 + 0.25
=13.25(10)
十进转二进制
与十转二的过程相反;
二进制转换时的坑
整数转换是没有问题的,但是小数转换的话,就很容易出事,我们下面讲的三个问题都是与小数转换相关的; 例如 :
0.1.toString(2) =
"0.0001100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1101" //为什么最后的是1 ?
0.1.toString(2).length // 57
这是因为我们的IFEE 754规定最多只可以显示53位数字(其中第一位一定是1)所以我们通常说是52位,如果出现了无限循环数的话会作进位处理.
这里我们看例子
1.1.toString(2)
"1.000110011001100110011001100110011001100110011001101"
1.1.toString(2).length
53
0.1.toString(2)
"0.0001100110011001100110011001100110011001100110011001101"
0.1.toString(2).length
57
这里我们留意1.1的二进制结果,长度是53,如果去除是.则是52,我们上面说过IFEE 754是可以显示53位的!,为什么? 因为进位了,如果不考虑进位的情况下结果应该是这样的:
1.00011001100110011001100110011001100110011001100110011 0011 0011 //无限循环的结果
1.0001100110011001100110011001100110011001100110011001 //截取1后面的53位非0数字结果应该是这样
1.0001100110011001100110011001100110011001100110011010 //在上面的结果上取52位,因为被截取的是1,所以进位后的结果就是这样了
1.000110011001100110011001100110011001100110011001101 // 因为最后的0是没有意义的,所以最终的结果就是这样
0.1 的二进制57位也是同样的道理
问题:
0.1 + 0.2 === 0.3 === false
0.075.toFixed(2) === 0.08 === false
0.075 * 3 === 0.22499999999999998 === true
计算背后的做了什么
理解了上面的IFEE 754和进位之后,就很容易理解0.1 + 0.2 的问题了,
"0.0001100110011001100110011001100110011001100110011001101" // 0.1
"0.001100110011001100110011001100110011001100110011001101" // 0.2
"0.0100110011001100110011001100110011001100110011001100111" // 两个二进制相加
"0.010011001100110011001100110011001100110011001100110011" // 我们期待的结果0.3
用最后两个数字相比,相加的结果多了一个1,根据我们前面说的,只能取第一个1后面的52位数字,最后一个1就得进位,得出的结果就是
"0.0100110011001100110011001100110011001100110011001101" 所以0.1 + 0.2 = 0.30000000000000004
===0.3
我们有办法处理么小数溢出?
一个好像有用的方法的方法
0.1 + 0.2 = (0.1 * 10 + 0.2 * 10) / 10
这种方法好像有效,最起码对部分是有效的,但是现实告诉我们,~~不行
(0.1+0.2).toFixed(8)*1
如果我们想混的话。这种方法能应付绝大部分我们遇到的小数位溢出的问题。
大蕉 math.js的
To be continue...
我们如何处理toFixed的问题
Number.prototype.toFixed = function(exponent){
if(this % 1 === 0){
return this + '.000000000000000000000'.substr(0,exponent + 1);
}
let block = '00000000000000000000';
let _this = this + '';
let idx = _this.indexOf('.')
if(idx + exponent <= _this.length){
exponent = Math.pow(10,exponent);
let addition = this > 0 ? .5 : -.5;
return Math.floor(this * exponent + addition) / exponent ;
}
return (_this + block).substr(0,idx + 1 + exponent);
}