二进制编程基础数据结构计算机原理
💾 为什么要学习二进制?
二进制是计算机的"母语"。无论你写的是Python、JavaScript还是C++,最终都会被转换成二进制在CPU中执行。理解二进制转换,能帮你:
- 🔍 调试底层问题:理解内存布局和数据存储
- ⚡ 性能优化:使用位运算提升代码效率
- 🛡️ 网络编程:处理二进制协议和数据包
- 🎮 图形编程:理解颜色值和像素数据
- 🔐 密码学:理解加密算法的基础
🔢 进制系统基础
常见进制对照表
| 十进制 | 二进制 | 八进制 | 十六进制 | 说明 |
|---|---|---|---|---|
| 0 | 0000 | 0 | 0 | 零 |
| 1 | 0001 | 1 | 1 | 一 |
| 2 | 0010 | 2 | 2 | 二 |
| 3 | 0011 | 3 | 3 | 三 |
| 4 | 0100 | 4 | 4 | 四 |
| 5 | 0101 | 5 | 5 | 五 |
| 8 | 1000 | 10 | 8 | 八 |
| 10 | 1010 | 12 | A | 十 |
| 15 | 1111 | 17 | F | 十五 |
| 16 | 10000 | 20 | 10 | 十六 |
| 255 | 11111111 | 377 | FF | 最大单字节 |
进制转换原理
十进制 → 二进制:除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 (向下取整)
📊 数据类型与存储
整数类型
| 类型 | 字节数 | 位数 | 范围 |
|---|---|---|---|
byte | 1 | 8 | -128 到 127 |
short | 2 | 16 | -32,768 到 32,767 |
int | 4 | 32 | -2,147,483,648 到 2,147,483,647 |
long | 8 | 64 | -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浮点数标准详解
- 计算机组成原理
- 位运算算法题精选
- 网络字节序深入理解