function c10to2 (num) {
// 整数
const numInteger = Math.floor(num)
// 小数
const numDecimal = num - numInteger
let integers = []
if (numInteger === 0) {
integers = ['0']
} else {
let integerVal = numInteger
while(integerVal !== 1) {
integers.push(integerVal % 2 === 0 ? '0' : '1')
integerVal = Math.floor(integerVal / 2)
integers.push('1')
const resInteger = integers.reverse().join('')
let decimals = []
if (numDecimal) {
let decimalVal = numDecimal
// 最多取49位的长度
let count = 49
while (decimalVal !== 1 && count > 0) {
decimalVal = decimalVal * 2
if (decimalVal >= 1) {
decimals.push('1')
if (decimalVal > 1) {
decimalVal = decimalVal - 1
} else {
decimals.push('0')
count--
const resDecimal = decimals.join('')
return resInteger + (resDecimal ? ('.' + resDecimal) : '')
小数在转换成二进制时,会存在无限循环的问题,上面的代码里截取了前49个值。
所以,这里就会引出了一个问题,就是常见的一个数字精度问题:0.1 + 0.2 != 0.3
。
0.1+ 0.2 != 0.3
直接看一下 0.1
转二进制:
0.1 × 2 = 0.2
0.2 × 2 = 0.4
0.4 × 2 = 0.8
0.8 × 2 = 1.6
0.6 × 2 = 1.2
0.2 × 2 = 0.4 // 循环开始
0.4 × 2 = 0.8
0.8 × 2 = 1.6
0.6 × 2 = 1.2
0.2
转二进制:
0.2 × 2 = 0.4
0.4 × 2 = 0.8
0.8 × 2 = 1.6
0.6 × 2 = 1.2
0.2 × 2 = 0.4 // 循环开始
0.4 × 2 = 0.8
0.8 × 2 = 1.6
0.6 × 2 = 1.2
因为无法得到1,可以发现有限十进制小数, 0.1
转换成了无限二进制小数 0.00011001100...
,0.2
转成了 0.001100110011...
。
由于无限循环,必然会导致精度丢失,正好 0.1 + 0.2
计算得到的数字在丢失精度后的最后一位不为0,所以导致结果为:0.30000000000000004
。
如果截取精度后最后一位为0,那自然就不存在结果不相等的情况,如 0.1 + 0.6 === 0.7
,事实上,0.1和0.6转二进制后都会丢失精度,但截取到的数值都是0,所以相等。
同样不相等的还设有 0.1 + 0.7 !== 0.8
等等。
所以是计算时转二进制的精度丢失,才导致的 0.1 + 0.2 !== 0.3
。
在 JavaScript 中所有数值都以 IEEE-754 标准的 64 bit 双精度浮点数进行存储的。
IEEE 754 标准的 64 位双精度浮点数的小数部分最多支持53位二进制位。
因浮点数小数位的限制而需要先截断二进制数字,再转换为十进制,所以在进行算术计算时会产生误差。
这里能看到,如果十进制小数要被转化为有限二进制小数,那么它计算后的小数第一位数必然要是 5
结尾才行(因为只有 0.5 × 2
才能变为整数)。
二进制数字转换成十进制
方法是:将二进制分成整数和小数两部分,分别进行转换,然后再组合成结果的十进制数值。
整数部分:这里直接使用 parseInt
函数,parseInt('1011', 2) => 11
。
小数部分:如 1011.001
的小数位 001
,使用下表的计算方式。
小数部分|0|0|1
--|--|--|--
基数的位数次幂|2-1|2-2|2^-3
每位与基数乘积|0 × (2^-1)|0 × (2-2)|1×(2-3)
每位乘积结果|0|0|0.125
最后的结果是每位乘积结果相加:0+0+0.125 = 0.125
。
整数与小数合起来,就得到了 1011.001
的十进制数字:11.125
。
根据规则,代码实现如下所示:
function c2To10 (binaryStr = '') {
if (typeof binaryStr !== 'string' || binaryStr === '') {
return NaN
const [ binIntStr, binDecStr ] = binaryStr.split('.')
let binDecimal = 0
if (binDecStr) {
binDecimal = [...binDecStr].reduce((res, val, index) => {
res += Number(val) * (2 ** (-(index + 1)))
return res
}, 0)
return parseInt(binIntStr, 2) + binDecimal