十进制转 IP
把 32 位十进制整数还原为点分十进制 IPv4 地址。输入 0 到 4,294,967,295 之间的任意值,四个字节会替你算出来。
把这个数重新拆成四个字节
十进制形式是一个数,所以反过来做就是从中取出四个字节。因为每个字节是八位,每个字节都是 256 进制的一位,于是用反复相除来提取 —— 就像用除以 10 的办法从十进制数里逐位取出数字一样。
3,232,235,777 ÷ 16,777,216 = 192 余 10,048,513
10,048,513 ÷ 65,536 = 168 余 65,281
65,281 ÷ 256 = 1 余 1
1 = 1
字节:192 . 168 . 1 . 1更快的一条路是走十六进制:地址永远是八位十六进制数字,每两位就是一个字节。3,232,235,777 是 C0A80101,直接拆成 C0、A8、01、01 —— 也就是 192、168、1、1。
完整示例:一个大于 20 亿的值
最高位为 1 的值,正是有符号整数实现会翻车的地方,所以值得完整推一遍。
示例:2,130,706,433
2,130,706,433 ÷ 16,777,216 = 127 余 2,650,401。接着 2,650,401 ÷ 65,536 = 40 余 24,961。再 24,961 ÷ 256 = 97 余 129。129 就是最后一个字节。
结果:127.0.40.129
写成十六进制是 7F002881,开头的 7F 也印证了第一个字节是 127 —— 环回地址范围。注意 2,130,706,433 明显小于 2^31 = 2,147,483,648,所以这个值恰好不会在有符号 32 位类型上出问题;一旦达到或超过这个阈值就不行了,这也是 127.255.255.255 是有符号 int 里最后一个安全地址的原因。
这个边界值得记住:正好 2,147,483,647 对应 127.255.255.255,而紧接着的下一个整数 2,147,483,648 就是 128.0.0.0 —— 第一个会溢出有符号 32 位整数的地址。
常见错误
- 传入了负数。先加上 2^32 还原,或者改用无符号类型。
- 把十进制和八进制搞混。某些语言里前导零会让字面量变成八进制,所以写成带前导零的 3232235777,在不同运行时下要么报错,要么变成一个完全不同的数。
- 传入了 IPv6 的整数。39 位数字意味着 128 位,它没有合法的 IPv4 解释,所以转换器会直接拒绝大于 4,294,967,295 的值,而不是静默截断。
- 把掩码当成地址。4,294,967,295 转换后是 255.255.255.255,它既是 /32 掩码也是广播地址 —— 同一个数出现在不同位置,含义并不相同。
参考:地址空间的形状
| 十进制 | 地址 | 意义 |
|---|---|---|
| 0 | 0.0.0.0 | 最小值;未指定地址与默认路由 |
| 1 | 0.0.0.1 | 次小值 |
| 2,130,706,433 | 127.0.0.1 | 环回地址 |
| 2,147,483,647 | 127.255.255.255 | 有符号 32 位整数中安全的最大值 |
| 2,147,483,648 | 128.0.0.0 | 第一个会溢出有符号 32 位整数的值 |
| 2,886,729,728 | 172.16.0.0 | RFC 1918 的 172.16/12 地址块起点 |
| 3,232,235,520 | 192.168.0.0 | RFC 1918 的 192.168/16 地址块起点 |
| 3,232,235,777 | 192.168.1.1 | 常见家庭网关 |
| 3,758,096,383 | 223.255.255.255 | 单播地址范围上界 |
| 3,758,096,384 | 224.0.0.0 | 组播地址起点(D 类) |
| 4,294,967,295 | 255.255.255.255 | 最大值;2^32 − 1 |
整个地址空间共有 2^32 = 4,294,967,296 个值。私有地址段占用:10/8 中的 16,777,216 个、172.16/12 中的 1,048,576 个、192.168/16 中的 65,536 个 —— 合计略多于 1,790 万,约占总量的 0.4%。
常见问题
如何把十进制数转成 IP 地址?
从高位开始把数字拆成四个字节。第一个字节是原值除以 16,777,216 的整数部分;减去该乘积后,用余数除以 65,536 得到第二个字节,再除以 256 得到第三个,最后的余数就是第四个。对 3,232,235,777 来说,得到 192、168、1、1。
十进制 IP 值的有效范围是多少?
0 到 4,294,967,295(含两端)。0 是 0.0.0.0,4,294,967,295 是 255.255.255.255。这个范围正好有 2^32 = 4,294,967,296 个值,与可能存在的 IPv4 地址总数一致。
为什么值为 0 时得到 0.0.0.0?
0 是一个合法的 IPv4 地址,含义是「本主机」;在路由表里则表示默认路由 0.0.0.0/0。它本身不能作为目的地址被路由,但确实是一个可以转换的合法值。
能转换 IPv6 的十进制值吗?
不能。IPv6 地址是 128 位,其十进制形式最多可达 39 位数字,无法放进 32 位整数。如果你的值超过十位数字,那几乎肯定是 IPv6 整数 —— 请使用 IPv6 计算器,它用任意精度运算处理完整的 128 位范围。
为什么要把地址存成数字?
因为整数比较既快又精确。区间判断退化成两次数值比较,排序在数值上才是正确的,而且只占固定的四个字节,而不是变长字符串。大多数大规模日志与分析系统都在写入时转成整数,原因正在于此。
如果我的数字在某处变成了负数怎么办?
给它加上 4,294,967,296。-2,147,483,648 到 -1 之间的负值,是地址空间上半部分某个地址的有符号 32 位表示,所以 -1,061,515,519 就是 3,232,235,777,也就是 192.168.1.1。