二进制转换完全指南:理解计算机的语言

深入理解二进制、十六进制与数字的转换,掌握大小端序和IEEE 754标准

优兔GOGO
2025年1月18日
开发教程
二进制编程基础数据结构计算机原理

💾 为什么要学习二进制?

二进制是计算机的"母语"。无论你写的是Python、JavaScript还是C++,最终都会被转换成二进制在CPU中执行。理解二进制转换,能帮你:

  • 🔍 调试底层问题:理解内存布局和数据存储
  • 性能优化:使用位运算提升代码效率
  • 🛡️ 网络编程:处理二进制协议和数据包
  • 🎮 图形编程:理解颜色值和像素数据
  • 🔐 密码学:理解加密算法的基础

🔢 进制系统基础

常见进制对照表

十进制二进制八进制十六进制说明
0000000
1000111
2001022
3001133
4010044
5010155
81000108
10101012A
15111117F十五
16100002010十六
25511111111377FF最大单字节

进制转换原理

十进制 → 二进制:除2取余,逆序排列
42 ÷ 2 = 21 余 0  ←─┐
21 ÷ 2 = 10 余 1  ←─┤
10 ÷ 2 = 5  余 0  ←─┤  读取方向
5  ÷ 2 = 2  余 1  ←─┤      ↑
2  ÷ 2 = 1  余 0  ←─┤      │
1  ÷ 2 = 0  余 1  ←─┘      │

结果:101010

二进制 → 十进制:按位权展开
101010 = 1×2⁵ + 0×2⁴ + 1×2³ + 0×2² + 1×2¹ + 0×2⁰
       = 32 + 0 + 8 + 0 + 2 + 0
       = 42

💻 各语言的进制表示

JavaScript

// 十进制
const dec = 42;

// 二进制(0b前缀)
const bin = 0b101010;

// 八进制(0o前缀)
const oct = 0o52;

// 十六进制(0x前缀)
const hex = 0x2A;

console.log(dec === bin);  // true
console.log(dec === oct);  // true
console.log(dec === hex);  // true

// 转换函数
(42).toString(2);   // "101010" (二进制)
(42).toString(8);   // "52" (八进制)
(42).toString(16);  // "2a" (十六进制)

parseInt('101010', 2);   // 42 (二进制转十进制)
parseInt('2A', 16);      // 42 (十六进制转十进制)

Python

# 不同进制表示
dec = 42
bin_num = 0b101010
oct_num = 0o52
hex_num = 0x2A

# 转换函数
bin(42)   # '0b101010'
oct(42)   # '0o52'
hex(42)   # '0x2a'

# 去除前缀
bin(42)[2:]   # '101010'
hex(42)[2:]   # '2a'

# 字符串转数字
int('101010', 2)   # 42
int('2A', 16)      # 42

C/C++

#include <stdio.h>

int main() {
    // 不同进制表示
    int dec = 42;
    int bin = 0b101010;  // C++14+
    int oct = 052;
    int hex = 0x2A;
    
    // 输出不同进制
    printf("十进制: %d\n", dec);      // 42
    printf("八进制: %o\n", dec);      // 52
    printf("十六进制: %x\n", dec);    // 2a
    printf("十六进制: %X\n", dec);    // 2A (大写)
    
    return 0;
}

Java

// 不同进制表示
int dec = 42;
int bin = 0b101010;
int oct = 052;
int hex = 0x2A;

// 转换函数
Integer.toBinaryString(42);      // "101010"
Integer.toOctalString(42);       // "52"
Integer.toHexString(42);         // "2a"

Integer.parseInt("101010", 2);   // 42
Integer.parseInt("2A", 16);      // 42

🔀 位运算详解

基本位运算

// AND(与):都为1才为1
5 & 3        // 0101 & 0011 = 0001 = 1

// OR(或):有1就为1
5 | 3        // 0101 | 0011 = 0111 = 7

// XOR(异或):不同为1
5 ^ 3        // 0101 ^ 0011 = 0110 = 6

// NOT(非):取反
~5           // ~0101 = 1010 = -6 (补码)

// 左移:乘以2的幂
5 << 2       // 0101 << 2 = 010100 = 20 (5 * 2² = 20)

// 右移:除以2的幂
20 >> 2      // 010100 >> 2 = 0101 = 5 (20 / 2² = 5)

// 无符号右移
-5 >>> 1     // 填充0而非符号位

实用位运算技巧

// 1. 判断奇偶
n & 1 === 0  // 偶数
n & 1 === 1  // 奇数

// 2. 交换两个数(无需临时变量)
a = a ^ b;
b = a ^ b;  // b = (a ^ b) ^ b = a
a = a ^ b;  // a = (a ^ b) ^ a = b

// 3. 判断2的幂
n > 0 && (n & (n - 1)) === 0

// 4. 计算二进制中1的个数
function countOnes(n) {
  let count = 0;
  while (n) {
    count++;
    n = n & (n - 1);  // 清除最低位的1
  }
  return count;
}

// 5. 获取最低位的1
n & (-n)

// 6. 清除最低位的1
n & (n - 1)

// 7. 绝对值(仅限32位整数)
n >> 31 ? -n : n

// 8. 快速乘除2的幂
n << 3   // n * 8
n >> 2   // n / 4 (向下取整)

📊 数据类型与存储

整数类型

类型字节数位数范围
byte18-128 到 127
short216-32,768 到 32,767
int432-2,147,483,648 到 2,147,483,647
long864-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807

有符号数表示(补码)

正数:直接用二进制表示
+5 = 0000 0101

负数:补码表示(取反+1)
-5 的原码: 1000 0101
-5 的反码: 1111 1010  (除符号位,其余取反)
-5 的补码: 1111 1011  (反码+1)

为什么用补码?
- 简化加减法运算(5 + (-5) = 0)
- 统一0的表示(只有+0,没有-0)

JavaScript数字特殊性

// JavaScript所有数字都是64位浮点数
typeof 42;        // "number"
typeof 42.5;      // "number"

// 但位运算会转为32位整数
2147483647 | 0    // 最大32位整数
2147483648 | 0    // -2147483648 (溢出)

// 安全整数范围
Number.MAX_SAFE_INTEGER    // 2^53 - 1 = 9007199254740991
Number.MIN_SAFE_INTEGER    // -(2^53 - 1)

// BigInt处理大整数
const big = 9007199254740991n;
console.log(big + 1n);  // 9007199254740992n

🌐 大端序与小端序

概念解释

数字:0x12345678 (十六进制)

大端序(Big-Endian):高字节存储在低地址
内存地址:  0x00  0x01  0x02  0x03
存储内容:  12    34    56    78
特点:符合人类阅读习惯

小端序(Little-Endian):低字节存储在低地址
内存地址:  0x00  0x01  0x02  0x03
存储内容:  78    56    34    12
特点:Intel x86/x64架构默认

中间序(Middle-Endian):少见,已废弃

检测系统字节序

// JavaScript检测
function getEndianness() {
  const buffer = new ArrayBuffer(4);
  const view = new DataView(buffer);
  view.setUint32(0, 0x12345678, false);  // 大端序写入
  
  const bytes = new Uint8Array(buffer);
  if (bytes[0] === 0x12) {
    return 'Big-Endian';
  } else if (bytes[0] === 0x78) {
    return 'Little-Endian';
  }
}

console.log(getEndianness());  // 多数为 "Little-Endian"
// C语言检测
#include <stdio.h>

int main() {
    unsigned int x = 0x12345678;
    unsigned char *c = (unsigned char*)&x;
    
    if (*c == 0x78) {
        printf("Little-Endian\n");
    } else {
        printf("Big-Endian\n");
    }
    return 0;
}

网络字节序

// 网络传输统一使用大端序
// Node.js Buffer处理

// 写入(小端序)
const buf = Buffer.alloc(4);
buf.writeInt32LE(0x12345678, 0);
console.log(buf);  // <Buffer 78 56 34 12>

// 写入(大端序)
buf.writeInt32BE(0x12345678, 0);
console.log(buf);  // <Buffer 12 34 56 78>

// 读取
buf.readInt32LE(0);  // 305419896 (小端序读取)
buf.readInt32BE(0);  // 305419896 (大端序读取)

🧮 浮点数:IEEE 754标准

Float (32位) 结构

符号位 | 指数位 | 尾数位
  1   |   8    |   23
  
示例:3.14
十进制: 3.14
二进制: 11.001000111101011100001010...

符号位 S = 0 (正数)
指数   E = 1000 0000 (128,实际指数 = 128 - 127 = 1)
尾数   M = 1.10010001111010111000010... (隐含前导1)

完整表示:
0 10000000 10010001111010111000010
│ └──┬───┘ └──────────┬───────────┘
│    │               │
│    │               └─ 尾数(23位)
│    └───────────────── 指数(8位)
└────────────────────── 符号(1位)

Double (64位) 结构

符号位 | 指数位 | 尾数位
  1   |   11   |   52

表示范围更大,精度更高
最小值: ±5.0 × 10^-324
最大值: ±1.7 × 10^308

特殊值

// JavaScript浮点数特殊值
Infinity         // 正无穷
-Infinity        // 负无穷
NaN              // Not a Number

// 产生特殊值
1 / 0            // Infinity
-1 / 0           // -Infinity
0 / 0            // NaN
Math.sqrt(-1)    // NaN

// 判断
isFinite(1/0)    // false
isNaN(0/0)       // true

浮点数精度问题

// 经典问题
0.1 + 0.2        // 0.30000000000000004 ❌
0.1 + 0.2 === 0.3  // false

// 原因:0.1和0.2无法精确表示为二进制
0.1 (十进制) = 0.0001100110011... (二进制,无限循环)

// 解决方案1:误差范围比较
function floatEqual(a, b) {
  return Math.abs(a - b) < Number.EPSILON;
}

floatEqual(0.1 + 0.2, 0.3);  // true

// 解决方案2:转整数计算
(0.1 * 10 + 0.2 * 10) / 10  // 0.3

// 解决方案3:使用专门的库
// npm install decimal.js
const Decimal = require('decimal.js');
new Decimal(0.1).plus(0.2).toNumber();  // 0.3

🛠️ 实战应用

1. 权限系统(位掩码)

// 使用位运算管理权限
const PERMISSION = {
  READ:   1 << 0,  // 0001 = 1
  WRITE:  1 << 1,  // 0010 = 2
  DELETE: 1 << 2,  // 0100 = 4
  ADMIN:  1 << 3   // 1000 = 8
};

// 授予权限
let userPermission = 0;
userPermission |= PERMISSION.READ;   // 添加读权限
userPermission |= PERMISSION.WRITE;  // 添加写权限

// 检查权限
function hasPermission(user, permission) {
  return (user & permission) === permission;
}

hasPermission(userPermission, PERMISSION.READ);   // true
hasPermission(userPermission, PERMISSION.DELETE); // false

// 移除权限
userPermission &= ~PERMISSION.WRITE;  // 移除写权限

// 切换权限
userPermission ^= PERMISSION.READ;  // 有则删,无则加

2. 颜色值处理

// RGB颜色 = 0xRRGGBB
const color = 0xFF5733;  // 橙红色

// 提取RGB分量
const r = (color >> 16) & 0xFF;  // 255
const g = (color >> 8) & 0xFF;   // 87
const b = color & 0xFF;          // 51

// 组合RGB
function rgbToHex(r, g, b) {
  return (r << 16) | (g << 8) | b;
}

rgbToHex(255, 87, 51);  // 0xFF5733

// RGBA(含透明度)
const rgba = 0xFF5733CC;  // CC = 80% opacity
const a = rgba & 0xFF;    // 204

3. 网络IP地址

// IPv4地址转换
function ipToInt(ip) {
  return ip.split('.')
    .map(Number)
    .reduce((acc, val) => (acc << 8) | val, 0) >>> 0;
}

function intToIp(int) {
  return [
    (int >>> 24) & 0xFF,
    (int >>> 16) & 0xFF,
    (int >>> 8) & 0xFF,
    int & 0xFF
  ].join('.');
}

const ip = '192.168.1.1';
const num = ipToInt(ip);     // 3232235777
intToIp(num);                // '192.168.1.1'

// 检查IP是否在子网内
function inSubnet(ip, subnet, mask) {
  const ipInt = ipToInt(ip);
  const subnetInt = ipToInt(subnet);
  const maskInt = ipToInt(mask);
  
  return (ipInt & maskInt) === (subnetInt & maskInt);
}

inSubnet('192.168.1.100', '192.168.1.0', '255.255.255.0');  // true

4. 数据压缩(位打包)

// 将多个小数字打包到一个整数中
// 例如:存储8个4位数字到一个32位整数

function pack4bits(...values) {
  let result = 0;
  for (let i = 0; i < values.length; i++) {
    result |= (values[i] & 0xF) << (i * 4);
  }
  return result >>> 0;
}

function unpack4bits(packed, count) {
  const result = [];
  for (let i = 0; i < count; i++) {
    result.push((packed >> (i * 4)) & 0xF);
  }
  return result;
}

const packed = pack4bits(15, 7, 3, 1);  // 打包4个数字
console.log(packed.toString(16));       // "1375"
unpack4bits(packed, 4);                 // [15, 7, 3, 1]

🔗 相关工具

需要快速进行数字与二进制转换?试试我们的 数字转二进制工具,支持:

  • 🔢 int、long、short、float、double多种数据类型
  • 🔄 数字 ↔ 二进制/十六进制双向转换
  • 🌐 大端序/小端序选择
  • 📋 详细字节表示
  • ⚡ 符合IEEE 754标准

推荐阅读

  • IEEE 754浮点数标准详解
  • 计算机组成原理
  • 位运算算法题精选
  • 网络字节序深入理解