js实现进制转换(含小数)

770 阅读1分钟

将n进制转换成10进制数

// 流程
// 1. 先对参数进行校验,数字必须是字符串,radix要在2-36之间
// 2. 去除前后空格、检验符号、16进制要去除0x,然后要去除前面所有0
// 3. 分出整数部分和小数部分
// 4. 累加,注意10以上的进制要额外写函数转换,如果是正数,小数部分是相加、否则相减

function transformToTen(num, fromRadix) {
  if (typeof num !== 'string') throw new Error()
  if (!num || !fromRadix || fromRadix === 10) return num
  if (fromRadix < 2 || fromRadix > 36) { throw new TypeError('The fromRadix range is limited to 2-36.') }

  num = num.trim()
  let isPositive = true
  if (/^[+-]/.test(num)) {
    isPositive = /^[+]/.test(num)
    num = num.slice(1)
  }
  if (fromRadix === 16 && /^0x/.test(num)) {
    num = num.slice(2)
  }
  num = num.replace(/^0/, '')

  function exchange(letter) {
    const code = letter.charCodeAt(0)
    if (letter >= 'A' && letter <= 'Z') {
      return code - 'A'.charCodeAt(0) + 10
    } else if (letter >= 'a' && letter <= 'z') {
      return code - 'a'.charCodeAt(0) + 10
    } else {
      return code - '0'.charCodeAt(0)
    }
  }

  let total = 0
  const str = num.toString()
  const integer = str.split('.')[0] ? str.split('.')[0].split('') : []
  const decimal = str.split('.')[1] ? str.split('.')[1].replace(/0+$/, '').split('') : []

  for (const [index, letter] of integer.entries()) {
    const num = fromRadix > 10 ? exchange(letter) : +letter
    total += num * Math.pow(fromRadix, integer.length - index - 1)
  }

  for (const [index, letter] of decimal.entries()) {
    const num = fromRadix > 10 ? exchange(letter) : +letter
    if (isPositive) {
      total += num * Math.pow(fromRadix, -(index+1))
    } else {
      total -= num * Math.pow(fromRadix, -(index+1))
    }
  }

  return isPositive ? total : -total
}

将10进制转换成n进制数

// 流程
// 1. 入参校验、num必须是数字类型,radix是否有值/范围/是否是10
// 2. 分出正负
// 3. 分出整数和小数
// 4. 整数部分一直除,每次拿余数入队列
// 5. 小数部分一直乘,每次拿相乘的整数部分入栈
// 6. 最终返回的要补齐前缀

function transformFromTen(num, toRadix, decimalCounts = 0) {
  if (typeof num !== 'number') throw new TypeError()
  if (!num || !toRadix || toRadix === 10) return num
  if (toRadix < 2 || toRadix > 36) { throw new Error() }

  const fill = (str) => toRadix === 16 ? `0x${str}` : toRadix === 8 ? `0${str}` : str
  const getLetter = (num) => num >= 10 ? String.fromCharCode('a'.charCodeAt(0) + num - 10) : `${num}`
  const isPositive = num > 0
  num = Math.abs(num)
  let integer = Math.floor(num)
  let decimal = num - integer
  let str = ''
  
  // 整数
  while (integer >= toRadix) {
    const yu = integer % toRadix
    str = `${getLetter(yu)}${str}`
    integer = Math.floor(integer / toRadix)
  }
  str = `${getLetter(integer)}${str}`
  if (!decimal || !decimalCounts) return fill(str)

  // 小数
  if (decimalCounts) str += '.'
  for (let i = 1; i <= decimalCounts; i++) {
    const num = Math.floor(toRadix * decimal)
    str = `${str}${getLetter(num)}`
    decimal = toRadix * decimal - num
  }

  return isPositive ? fill(str) : `-${fill(str)}`
}

n进制之间相互转换

function transform(num, fromRadix, toRadix, decimalCounts) {
  const ten = transformToTen(num, fromRadix)
  return transformFromTen(ten, toRadix, decimalCounts)
}

参考 / 引用 / 测试