前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库Number toString radix 进制转换
JavaScript值与类型

怎么把十进制数转成二进制、八进制、十六进制字符串?有什么限制?

调用 num.toString(radix),radix 传 2、8、16 分别得到二进制、八进制、十六进制字符串。radix 只能是 2 到 36,超出范围抛 RangeError;负数只会在前面加负号,不会给出补码。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#值与类型
先看核心答案读代码示例
理解线索

`toString(radix)` 的三条边界

  1. radix 范围2~36,大于 10 的部分用 a~z 表示,越界抛 RangeError
  2. 负数和小数负数只加负号;小数也能转,但可能是一长串近似位
  3. 精度上限超过 Number.MAX_SAFE_INTEGER 的整数本身就存不准,要改用 BigInt

直接写 255.toString(16) 是语法错误,解析器会把第一个点当成小数点。要写成 (255).toString(16)、255..toString(16) 或先存进变量。

核心回答

先记住这个答案

数字自带的 toString 方法接收一个可选参数 radix,表示目标进制。(255).toString(2) 得到 '11111111',传 8 得到 '377',传 16 得到 'ff',不传就是十进制。radix 只能是 2 到 36 之间的整数,因为 0~9 加 a~z 一共 36 个字符,超出范围会抛 RangeError。另外要记住三点:负数只会在结果前面加负号,小数也能转但可能很长,超过 2 的 53 次方的整数本来就存不准,转出来的结果也不可信。

  • toString 的进制范围是 2 到 36
  • 负数结果是负号加绝对值,不是补码
  • 要看补码先 >>> 0 再转
  • 结果不补 0,定宽输出用 padStart

常用进制和几个边界值

先把最常见的几种写法和边界一起跑一遍,结果都是字符串。

一次看完常用进制JavaScript
const n = 255
console.log(n.toString(2))        // 二进制
console.log(n.toString(8))        // 八进制
console.log(n.toString(16))       // 十六进制
console.log((10).toString(36))    // 36 进制用到字母 a~z
console.log((-255).toString(16))  // 负数只是前面加负号
console.log((0.5).toString(2))    // 小数也能转
try {
  n.toString(37)                  // radix 只能是 2~36
} catch (e) {
  console.log(e.name)
}
查看输出与解释
11111111
377
ff
a
-ff
0.1
RangeError

字母统一是小写,要大写的话自己再调一次 toUpperCase()。-255 的结果是 '-ff',说明 toString 做的是「符号加绝对值」的转换,和计算机里存负数用的补码不是一回事。0.5 转二进制是 '0.1',因为 0.5 正好是二分之一。换成 0.1 就不一样了,(0.1).toString(2) 会得到一长串 0.000110011001100…,它在二进制里是无限循环小数,只能截断存储。 radix 传小数时会先取整,(255).toString(16.9) 按 16 处理,但这种写法没有意义,面试时知道有这回事就行。

项目里真正会遇到的写法

实际开发里单纯转进制很少出问题,出问题的是位数、补码和大数。比如把颜色分量转成 #rrggbb,(5).toString(16) 只得到 '5',拼出来的颜色值就少了一位。

补位、看补码和处理大数JavaScript
// 补齐位数:toString 不会自动补 0
console.log((5).toString(2).padStart(8, '0'))
// 看负数的补码:先用 >>> 0 转成 32 位无符号整数
console.log((-5 >>> 0).toString(16))
console.log((-1 >>> 0).toString(2).length)
// 反过来:parseInt 按指定进制解析
console.log(parseInt('ff', 16), parseInt('11111111', 2))
// 超过 2^53 的整数,Number 已经存不准了
console.log((2 ** 53 + 1).toString(16))
console.log((2n ** 53n + 1n).toString(16))
查看输出与解释
00000101
fffffffb
32
255 255
20000000000000
20000000000001

padStart(8, '0') 是定宽输出的标准写法,颜色值用 padStart(2, '0')。 >>> 0 是无符号右移 0 位,作用是把数字按 32 位整数重新解释成无符号数。-5 的 32 位补码是 0xfffffffb,这样就能在调试位运算时看到真实的位模式。注意它只对 32 位范围有效。 2 ** 53 + 1 在 Number 里存不下,已经被舍成了 2 ** 53,所以十六进制结果末位是 0。这不是 toString 的错,是数字在转换之前就已经不对了。处理 ID、哈希这类大整数时要用 BigInt,BigInt 同样支持 toString(radix)。

转换是双向的
toString(radix) 负责数字转字符串,反方向用 parseInt(str, radix)。两者的进制范围一致,都是 2~36。parseInt 遇到不属于该进制的字符会停下,比如 parseInt('12', 2) 只认第一个 1,结果是 1。
回答前,多想一步

容易答错的地方

以为负数会转出补码
很多人以为 (-5).toString(2) 会得到一串 1 开头的二进制,实际结果是 '-101'。toString 不关心数字在内存里怎么存,只做数学意义上的进制转换。要看补码得先用 >>> 0 转成无符号整数。
直接在整数字面量后面点方法
255.toString(16) 会报 SyntaxError,因为解析器把 255. 当成了一个小数。加括号、写两个点,或者放进变量都行。255.5.toString(16) 反而能正常运行,因为第一个点已经被小数占用了。
把结果当数字用
toString 返回的永远是字符串。(255).toString(2) 得到的 '11111111' 如果直接参与加法,会变成字符串拼接。需要数值时再用 parseInt 按原进制解析回来。
试着用自己的话回答

面试官还会怎么问?

不用 toString,怎么手写十进制转二进制?

整数部分用「除 2 取余,逆序排列」:不断对 2 取余并把余数放到结果前面,直到商为 0。小数部分用「乘 2 取整,顺序排列」,每次乘 2 取出整数位,剩下的小数继续乘,要设一个最大位数,因为 0.1 这类数永远乘不完。

为什么 radix 最大是 36?

进制需要用字符表示每一位,JavaScript 选的是 0~9 加 26 个英文字母,一共 36 个。短链接、随机 ID 常用 toString(36),就是因为它是内置支持的最紧凑的表示。

字面量能直接写二进制和八进制吗?

可以。ES2015 起 0b1010 是二进制,0o17 是八进制,0xff 是十六进制,它们都在解析时直接变成普通的 Number,和十进制写法没有区别。老式的 017 八进制写法在严格模式下是语法错误,不要再用。

从一道题,走向一组知识

把知识连起来

值与类型

为什么 0.1 + 0.2 不等于 0.3?浮点数该怎么比较?

(0.1).toString(2) 是一长串循环位,正好解释了 0.1 + 0.2 为什么不等于 0.3。

值与类型

Number 能表示的最小正数是多少?为什么会有 +0 和 -0?

两题都在讲 Number 的表示边界,一个看进制输出,一个看最小正数和正负零。

参考资料

  • JavaScript:值与类型参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 常用进制和几个边界值
  3. 项目里真正会遇到的写法
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑