写在前面
- 简单记录一下,Javascript parseInt+parseFloat 内部实现,并实现 convertStringToNumber
实践准备
- 首先我们需要简单梳理一下我们的实现过程,再根据 ECMAScript-262 标准完善实现
- input: @params: { str } 输入需要转换的字符串, { radix } 转换的指定基数
- 对 input 的简单处理 StringNumericLiteral
- 规格化 str
- 将 str 中可能出现的 StrWhiteSpace 去掉
- 将 str 中可能出现的 StrWhiteSpace 去掉
- 判断 radix 是否合法,仅支持 Decimal / BinaryInteger / OctalInteger / HexInteger radix
- Decimal: 10
- BinaryInteger: 2
- OctalInteger: 8
- HexInteger: 16
- 简单算法处理过程
- 十进制数
- 处理 Infinity 情况
- 符号位处理
- 小数点
- 科学计数法
- 二进制数
- 八进制数
- 十六进制数
- 十进制数
- output: return number
详细实践
- 根据上面的实践准备
1 | /* |
将 str 中的 whitespace 进行匹配
1
2
3
4
5
6
7
8
9
10/*
* @params: { str } 输入需要替换的字符串
* return: resStr 无 whitespace 字符串
*/
function replaceWhiteSpaceInStr(str) {
let resStr = str.replace(/\s*/g, '') // 去除空格
// resStr = resStr.replace(/^[\u000A|\u000D|\u2028|\u2029]/g, '') // 去除 LineTerminator unicode输入方式
resStr = resStr.replace(/[\r|\n]/g, '') // 去除换行
return resStr
}- 单元测试
- 单元测试
将 str 根据指定基数进行转换
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46/*
* @params: { str } 输入需要替换的字符串, { radix } 转换的指定基数
* return: resStr 根据指定基数转换过得字符串
*/
function formatStrByRadix(str, radix) {
let resStr = ''
let testReg = null
switch (radix) {
case 10:
testReg = /^((0)|([1-9][0-9]*))?.?([0-9]*)((e|E)?(\+|\-)?([0-9]*))?/
break;
case 2:
// 根据 ECMA-262 是 /^0(b|B)(0|1)+$/
// 但是我在 Chrome 浏览器上测了,二进制没有 'b' or 'B'
testReg = /^(0|1)+/
break;
case 8:
// 根据 ECMA-262 是 /^0(O|o)[0-7]+$/
// 但是我在 Chrome 浏览器上测了,二进制没有 'o' or 'O'
testReg = /^[0-7]+/
break;
case 16:
// 根据 ECMA-262 是 /^0(x|X)([0-9a-fA-F])+/
// 但是我在 Chrome 浏览器自测中发现,没有表示符 'x' or 'X'也可
testReg = /^0(x|X)?([0-9a-fA-F])+/
break;
default:
console.log(`radix: ${radix}, str: ${str} illegal radix`)
return false
}
resStr = (testReg.exec(str) && testReg.exec(str)['0']) || 'convert fail'
console.log(`radix: ${radix}, ${str} --> resStr: `, resStr)
}
formatStrByRadix('001010101', 3)
formatStrByRadix('001010101', 2)
formatStrByRadix('001010101', 8)
formatStrByRadix('0b01010101', 2)
formatStrByRadix('012345677', 8)
formatStrByRadix('012345677', 2)
formatStrByRadix('0o12345677', 8)
formatStrByRadix('1.01E+23', 10)
formatStrByRadix('.012345677', 10)
formatStrByRadix('.012345677', 10)
formatStrByRadix('0123acfACF', 16)
formatStrByRadix('0x123acfACF', 16)
formatStrByRadix('0x123acfACFG', 16) 单元测试
-
根据指定基数进行相应算法转换
二进制:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 将 str 转成二进制
function converStringToBinaryInteger(str, radix) {
return strMultipleRadix(str, radix)
}
/*
* @params: { str } 输入需要替换的字符串, { radix } 转换的指定基数
* return: resStr 乘以指定基数转换过得字符串
*/
function strMultipleRadix(str, radix) {
let tempStrArr = str.split('')
let res = 0
for (let i = tempStrArr.length - 1; i > 0; i --) {
res += (tempStrArr[i].codePointAt(0) - '0'.codePointAt(0)) * Math.pow(radix, tempStrArr.length - i - 1)
}
return res
}
converStringToBinaryInteger('001010101', 2)- 单元测试
- 单元测试
八进制
同上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 将 str 转成八进制
function converStringToBinaryInteger(str, radix) {
return strMultipleRadix(str, radix)
}
/*
* @params: { str } 输入需要替换的字符串, { radix } 转换的指定基数
* return: resStr 乘以指定基数转换过得字符串
*/
function strMultipleRadix(str, radix) {
let tempStrArr = str.split('')
let res = 0
for (let i = tempStrArr.length - 1; i > 0; i --) {
res += (tempStrArr[i].codePointAt(0) - '0'.codePointAt(0)) * Math.pow(radix, tempStrArr.length - i - 1)
}
return res
}
converStringToBinaryInteger('001010101', 2)单元测试
十六进制
需要对 a-f A-F 进行判断,别忘了+10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31// 将 str 转成十六进制
function converStringToBinaryInteger(str, radix) {
return strMultipleRadix(str, radix)
}
/*
* @params: { str } 输入需要替换的字符串, { radix } 转换的指定基数
* return: resStr 乘以指定基数转换过得字符串
*/
function strMultipleRadix(str, radix) {
let tempStrArr = str.split('')
let res = 0
for (let i = tempStrArr.length - 1; i > 0; i--) {
if (radix === 16) {
if ((tempStrArr[i].codePointAt(0) - '0'.codePointAt(0) >= 0) && (tempStrArr[i].codePointAt(0) - '0'.codePointAt(0) < 10)) {
res += (tempStrArr[i].codePointAt(0) - '0'.codePointAt(0)) * Math.pow(radix, tempStrArr.length - i - 1)
} else if ((tempStrArr[i].codePointAt(0) - 'a'.codePointAt(0) >= 0) && (tempStrArr[i].codePointAt(0) - 'a'.codePointAt(0) < 6)) {
console.log('(tempStrArr[i].codePointAt(0)a', tempStrArr[i].codePointAt(0) - 'a'.codePointAt(0))
res += (tempStrArr[i].codePointAt(0) - 'a'.codePointAt(0) + 10) * Math.pow(radix, tempStrArr.length - i - 1)
} else if ((tempStrArr[i].codePointAt(0) - 'A'.codePointAt(0) >= 0) && (tempStrArr[i].codePointAt(0) - 'A'.codePointAt(0) < 6)) {
console.log('(tempStrArr[i].codePointAt(0)A', tempStrArr[i].codePointAt(0) - 'A'.codePointAt(0))
res += (tempStrArr[i].codePointAt(0) - 'A'.codePointAt(0) + 10) * Math.pow(radix, tempStrArr.length - i - 1)
}
} else {
res += (tempStrArr[i].codePointAt(0) - '0'.codePointAt(0)) * Math.pow(radix, tempStrArr.length - i - 1)
}
}
return res
}
console.log(converStringToBinaryInteger('001010101', 16))
console.log(converStringToBinaryInteger('00101acfAFF', 16))单元测试
最终二/八/十六进制根据指定基数进行相应算法转换方法为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22/*
* @params: { str } 输入需要替换的字符串, { radix } 转换的指定基数
* return: resStr 乘以指定基数转换过得字符串
*/
function strMultipleRadix(str, radix) {
let tempStrArr = str.split('')
let res = 0
for (let i = tempStrArr.length - 1; i > 0; i--) {
if (radix === 16) {
if ((tempStrArr[i].codePointAt(0) - '0'.codePointAt(0) >= 0) && (tempStrArr[i].codePointAt(0) - '0'.codePointAt(0) < 10)) {
res += (tempStrArr[i].codePointAt(0) - '0'.codePointAt(0)) * Math.pow(radix, tempStrArr.length - i - 1)
} else if ((tempStrArr[i].codePointAt(0) - 'a'.codePointAt(0) >= 0) && (tempStrArr[i].codePointAt(0) - 'a'.codePointAt(0) < 6)) {
res += (tempStrArr[i].codePointAt(0) - 'a'.codePointAt(0) + 10) * Math.pow(radix, tempStrArr.length - i - 1)
} else if ((tempStrArr[i].codePointAt(0) - 'A'.codePointAt(0) >= 0) && (tempStrArr[i].codePointAt(0) - 'A'.codePointAt(0) < 6)) {
res += (tempStrArr[i].codePointAt(0) - 'A'.codePointAt(0) + 10) * Math.pow(radix, tempStrArr.length - i - 1)
}
} else {
res += (tempStrArr[i].codePointAt(0) - '0'.codePointAt(0)) * Math.pow(radix, tempStrArr.length - i - 1)
}
}
return res
}转换成十进制
实践思路
- 判断正负符号位
- 判断是否为 Infinity
- 判断是否为 整数
- 整数
- 是否为科学计数法表示
- 是,通过指数位置分成两部分:整数部分+指数部分
- 否,只需处理整数部分
- 是否为科学计数法表示
- 小数
- 是否为科学计数法表示
- 是,通过分割成小数点到指数,分成三部分:整数部分+小数部分+指数部分
- 否,通过小数点位置,分成两部分:整数部分+小数部分
- 是否为科学计数法表示
- 整数
代码大致为:
1 | function converStringToDeciaml(str) { |
判断正负,并根据符号位格式化字符串
1 |
|
根据符号位格式化字符串(移除字符号位)
1 | // 根据符号位格式化字符串 |
判断是否为Infinity
1 | // 是否为 Infinity |
切割字符串,分成整数+小数+指数位符号+指数部分
1 | // 切割字符串 |
整数部分运算
1 |
|
小数部分运算
1 |
|
指数部分运算
1 |
|
单元测试
- 测试用例
1 | console.log(converStringToDeciaml('1.0e+10')) |
- 测试截图
待解决问题
- 是的 😭 它的浮点数的舍入,我没有想到好的方法,哭唧唧
- 代码地址
写在后面
- 等我有空把博客评论搞一下,欢迎大家来告诉我的遗留问题该怎么解决,以及你们在参考我的实现时,有测试用例过不去的地方,也可以评论告诉我,我优化代码。
- 大家等不及的话,可以去我的 csdn 评论告诉我,卑微小彭,在线求解
- 祝大家多多发财