Skip to content

Commit bfd629a

Browse files
committed
IEEE 754 post
1 parent 4bc61f9 commit bfd629a

File tree

26 files changed

+1564
-72
lines changed

26 files changed

+1564
-72
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
+++
2+
date = '2025-08-19T8:00:00+08:00'
3+
draft = false
4+
title = 'IEEE-754 Introduce'
5+
tags = ['c']
6+
+++
7+
IEEE 754 标准数值类型及分类
8+
9+
## 整数 Integer
10+
整数是没有小数部分的值, 在计算机内通常有两种表示方式:
11+
- 有符号整数: 可以表示正数和负数, 最常用的是二补码表示. 例如 8 位二进制的范围为[-2^7, 2^7-1]
12+
- 无符号整数: 仅表示非负数, 8 位二进制的范围为 [0, 2^8-1]
13+
14+
其中补码(Two's Complement)用于表示负数
15+
- 正数补码与原码相同
16+
- 负数的补码 = 该数绝对值的二进制取反 + 1
17+
18+
通过补码, 可以使得加减运算统一, 溢出检测更加简单
19+
20+
## 浮点数 Floating-point
21+
浮点数用于表示带小数的实数, 尤其适合科学计算和近似表示很大或很小的数值. IEEE 754 定义了浮点数的标准格式, 类似科学计数法:
22+
```
23+
value = (-1)^(sign) x mantissa x 2^(exponent)
24+
```
25+
浮点数由三部分组成:
26+
1. 符号位 sign: 0/1代表正负
27+
2. 阶码 exponent: 通常使用偏移表示法
28+
3. 尾数 mantissa/significand: 小数部分
29+
30+
常见浮点数类型:
31+
- 单精度 float32: 1 位符号 + 8 位阶码 + 23 位尾数
32+
- 双精度 float64: 1 位符号 + 11 位阶码 + 52 位尾数
33+
34+
### 浮点数的分类 Categories
35+
以 64 精度为例
36+
| 部分 | 位数 | 描述 |
37+
| :---------------------- | :- | :---------------------- |
38+
| 符号位(sign) | 1 | 0 表示正数,1 表示负数 |
39+
| 阶码(exponent) | 11 | 偏移量(bias)为 1023,表示数值的量级 |
40+
| 尾数(fraction / mantissa) | 52 | 有效数字,不包括隐藏位 |
41+
42+
浮点数的表示公式:
43+
```
44+
value = (-1)^sign x (1 + fraction) x 2^(exponent - bias)
45+
```
46+
47+
1. 正常数 Normalized numbers
48+
- 阶码 exponent: 不全为0, 也不全为1, [1, 2^11-2]
49+
- 尾数 fraction: 隐含1, [0, 1 - 2^(-52)], (52位全1 位 1 - 2^(-52))
50+
```
51+
value = (-1)^sign x (1 + fraction) x 2^(exp - 1023)
52+
```
53+
其中, `bias = 2^(exponent - 1) - 1`, 这里为 1023
54+
55+
- 最大正常数: 阶码最大为 `1024*2-2 = 2046`, 尾数全为1 `1-2^(-52)`, 即 `( 1 + (1 - 2^(-52)) ) x 2^(2026-1023)`
56+
- 最小正常数: 阶码最小位 `1`, 尾数全为0, 即 `( 1 + 0 ) x 2^(1-1023)`
57+
58+
2. 非正规数 Subnormal numbers / Denormalized numbers
59+
- 阶码 exponent: 全0
60+
- 尾数 fraction: [0, 1-2^(-52)], 没有隐藏位1
61+
```
62+
value = (-1)^sign x (fraction) x 2^(1 - E_min)
63+
```
64+
其中, `E_min` 定义为 最小的正常数指数 = `1 - bias = -1022`
65+
> 注意: 非正规数的指数并不是阶码减 bias 的直接结果(0−1023 = −1023), 而是约定使用最小正常数指数 `E_min = −1022`.
66+
> 这样可以让非正规数顺接正常数, 形成连续的可表示范围, 并支持渐进下溢.
67+
68+
- 最大非正规数: 阶码为0, 尾数全为1, 即 `( 1 - 2^(-52) ) x 2^(-1022)`
69+
- 最小非正规数: 阶码位0, 尾数最低位为1, 其他为0, 即 `( 2^(-52) x 2^(-1022) )`
70+
71+
3. 零 0
72+
- 符号 sign: 0/1 正负零
73+
- 阶码 exponent: 0
74+
- 尾数 fraction: 0
75+
76+
4. 无穷 infinity
77+
- 符号sign: 0/1 正负无穷
78+
- 阶码 exponent: 2047 (全1)
79+
- 尾数 fraction: 0
80+
81+
5. 非数值 NaN
82+
- 阶码 exponent: 2047 (全1)
83+
- 尾数 fraction != 0
84+
85+
表示未定义或非法运算, 如 0/0
86+
87+
88+
| 类别 | 符号位 (sign) | 阶码 (exponent) | 尾数 (fraction / mantissa) | 描述 |
89+
| :---------------- | :--------- | :------------ | :----------------------- | :-------------------- |
90+
| 正常数 | 0 或 1 | 1\~2046 | 0\~(1 − 2^-52) | 阶码非全 0/全 1,尾数隐含最高位 1 |
91+
| 非正规数 | 0 或 1 | 0 | 0\~(1 − 2^-52) | 阶码全 0,尾数非零,无隐藏位,接近 0 |
92+
| ±0 | 0 或 1 | 0 | 0 | 正零或负零 |
93+
| ±∞ | 0 或 1 | 2047 | 0 | 正无穷或负无穷 |
94+
| NaN (qNaN / sNaN) | 0 或 1 | 2047 | 非零 | 阶码全 1,尾数非零,表示无效或未定义运算 |

content/posts/2025-08-19_redis-learn.md

Lines changed: 246 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
+++
22
date = '2025-08-19T8:00:00+08:00'
3-
draft = true
4-
title = 'Introduct Redis'
5-
tags = ["Redis", "Database"]
3+
draft = false
4+
title = 'Redis String'
5+
tags = ['Redis', 'Database']
66
+++
7-
8-
9-
# 数据结构与应用
7+
介绍Redis中的字符串键
108

119
## 字符串
1210
字符串建是 Redis 最基本的键值对类型, 这种类型的键值对会在数据库中把单独的一个值关联起来, 被关联的键和值可以为文本, 也可以是图片, 视屏, 音频等二进制数据.
1311

1412
- SET: 为字符串键设置值 O(1)
15-
> SET key value
13+
> SET key value
14+
1615
```Redis
1716
SET number "10086"
1817
> OK
@@ -31,7 +30,7 @@ tags = ["Redis", "Database"]
3130
```
3231

3332
- GET: 获取字符串键的值 O(1)
34-
> GET key
33+
> GET key
3534
3635
```Redis
3736
GET number
@@ -45,7 +44,7 @@ tags = ["Redis", "Database"]
4544
```
4645

4746
- GETSET: 获取旧值并更新值 O(1)
48-
> GETSET key new\_value
47+
> GETSET key new\_value
4948
5049
```Redis
5150
GETSET key "123456"
@@ -99,4 +98,242 @@ print(cache.get("daily_bing.jpg")[:20]) # 读取二进制数据的前20字节
9998
10099

101100
### 示例: 锁
101+
锁是一种同步机制, 用于保证一种资源任何时候只能被一个进程使用.
102+
一个锁的实现通常有获取 (acquire) 和释放 (relase) 这两种操作.
103+
104+
- 获取操作用于获取资源的独占使用权, 任何时候只能有一个进程取得锁, 此时, 取得锁的进程称为锁的持有者.
105+
- 释放操作用于放弃资源的独占使用权, 一般由持有者调用.
106+
107+
```Python
108+
from redis import Redis
109+
110+
VALUE_OF_LOCK = "locking"
111+
class Lock:
112+
def __init__(self, client, key):
113+
self.client = client
114+
self.key = key
115+
def acquire(self):
116+
result = self.client.set(self.key, VALUE_OF_LOCK, nx=True)
117+
return result is True
118+
def relase(self):
119+
return self.client.delete(self.key) == 1
120+
121+
client = Redis(decode_responses=True)
122+
lock = Lock(client, 'test-lock')
123+
print("第一次获取锁:", lock.acquire())
124+
125+
print("第二次获得锁:", lock.acquire())
126+
print("取消锁:", lock.relase())
127+
128+
print("第三次获得锁:", lock.acquire())
129+
```
130+
131+
> 第一次获取锁: True
132+
> 第二次获得锁: False
133+
> 取消锁: True
134+
> 第三次获得锁: True
135+
136+
若要设置锁的时间 `SET key value NX EX time` 这样是原子性语法, 删除操作对应命令是 `DEL key`, 返回0表示 key 不存在, 返回1~N表示删除key的数量.
137+
138+
NX 确保锁只有在没有值时加锁成功, 若有值则返回 None, 通过检查 result 是否为 True 来判断是否获得了锁.
139+
140+
141+
- MSET: 一次为多个字符串键设置值 O(N)
142+
> MSET key value [key value ...]
143+
144+
同 SET 命令, MSET 执行成功后返回 OK, 并且会用新值覆盖旧值. 由于执行多条 SET 命令要客户端和服务端之间多次进行网络通讯, 因此 MSET 能减少程序执行操作的时间
145+
146+
147+
- MGET: 一次获取多个字符串键的值 O(N)
148+
> MGET key [key ...]
149+
150+
- MSETNX: 只在键不存在的情况下, 一次为多个键设置值
151+
> MSETNX key value [key value ...]
152+
153+
若有任意一次键存在值, 则会取消所有操作, 并返回0. 只有所有键都没有值的时候, 执行才成功, 返回1.
154+
155+
### 示例: 存储文章信息
156+
在构建应用程序的时候, 经常会需要批量设计和获取多项信息, 以博客为例:
157+
- 当用户注册博客时, 程序将用户名字、帐号、密码、注册时间等存储起来, 并在登陆时查取这些信息.
158+
- 当编写一篇博客文章时, 就要将博客标题、内容、作者、发表时间存储起来, 并在用户阅读的时候取出这些信息.
159+
160+
通过 MSET、MSETNX、MGET 命令, 可以实现上面提到的这些批量设置和批量获取操作
161+
162+
```Python
163+
from redis import Redis
164+
from datetime import datetime
165+
166+
class Article:
167+
def __init__(self, client, article_id):
168+
"""根据id创建文章id"""
169+
self.client = client
170+
self.id = str(article_id)
171+
self.title_key = "article::" + self.id + "::title"
172+
self.content_key = "article::" + self.id + "::content"
173+
self.author_key = "article::" + self.id + "author"
174+
self.create_at_key = "article::" + self.id + datetime.now()
175+
176+
def create(self, title, content, author):
177+
"""创建文章"""
178+
article_data = {
179+
self.title_key: title,
180+
self.content_key: content,
181+
self.author_key: author,
182+
self.create_at_key: datetime.now(),
183+
}
184+
return self.client.msetnx(article_data)
185+
186+
def get(self):
187+
"""获取文章信息"""
188+
result = self.client.mget(
189+
self.title_key,
190+
self.content_key,
191+
self.author_key,
192+
self.create_at_key,
193+
)
194+
return {
195+
"id": self.id,
196+
"title": result[0],
197+
"content": result[1],
198+
"author": result[2],
199+
"create_at_key": result[3],
200+
}
201+
202+
def update(self, title=None, content=None, author=None):
203+
"""更新文章"""
204+
article_data = {}
205+
if title is not None:
206+
article_data[self.title_key] = title
207+
if content is not None:
208+
article_data[self.content_key] = content
209+
if author is not None:
210+
article_data[self.author_key] = author
211+
return self.client.mset(article_data)
212+
213+
client = Redis(decode_responses=True)
214+
article = Article(client, 10086)
215+
216+
# 创建文章
217+
print(article.create("message", "hello world", "sx"))
218+
219+
# 获取文章信息
220+
print(article.get())
221+
222+
# 更新文章作者
223+
print(article.update(author="join"))
224+
```
225+
上面程序使用了多个字符串键存储文章信息: `article::<id>::<attribute>`
226+
227+
228+
- STRLEN: 获取字符串的字节长度 O(1)
229+
> STRLEN key
230+
231+
对于存在的键, 返回字节长度信息. 对于不存在的键, 返回0
232+
233+
234+
- GETRANGE: 获取字符串值指定索引范围上的内容 O(N)
235+
> GETRANGE key start end
236+
237+
```Redis
238+
SET message "hello world"
239+
GETRANG message 0 4
240+
> hello
241+
242+
GETRANGE message -5 -1
243+
> world
244+
```
245+
246+
- SETRANGE: 修改字符串索引范围的值 O(N)
247+
> SETRANGE key index subsitute
248+
249+
```Redis
250+
set message "hello world"
251+
SETRANGE message 6 Redis
252+
> (integer) 11
253+
254+
GET message
255+
> hello Redis
256+
```
257+
258+
当用户给定的新内容比被替换内容长的时候, SETRANGE 会自动扩展被修改的字符串值
259+
```Redis
260+
SETRANGE message 5 ", this is a message"
261+
> (integer) 24
262+
263+
GET message
264+
> "hello, this is a message"
265+
```
266+
267+
当用户给出的索引长度超出被替换字符长度时, 字符串末尾到 index-1 之间部分将使用空字符串填充为0
268+
```Redis
269+
SET greeting "hello"
270+
SETRANGE greeting 10 "hello"
271+
> (integer) 15
272+
273+
GET greeting
274+
> "hello\x00\x00\x00\x00\x00world"
275+
```
276+
277+
### 示例: 给文章存储程序加上文章长度计数功能和文章御览功能给
278+
- 文章长度计数功能: 显示文章长度, 用于估计阅读时长
279+
- 文章预览功能: 显示文章开头一部分内容, 帮助读者快速了解文章
280+
281+
```Python
282+
class Article:
283+
...
284+
285+
def get_content_len(self):
286+
return self.client.strlen(self.content_key)
287+
288+
def get_content_perview(self, preview_len):
289+
start_index = 0
290+
end_index = preview_len - 1
291+
return self.client.getrange(self.content, start_index, end_index)
292+
```
293+
294+
295+
- APPEND: 追加新内容到值的末尾
296+
> APPEND key suffix
297+
298+
若用户给定的 key 不存在, 则会先将键执行追加操作
299+
300+
301+
### 示例: 存储日志
302+
很多程序运行的时候会产生日志, 日志记录了程序的运行状态以及执行过的重要操作.
303+
若每条日志存储一个键值对, 则会消耗很多资源, 且分散在数据库中, 需要额外的时间查找日志, 这里将不同日志拼接在同一个值里面.
304+
```Python
305+
from redis import Redis
306+
307+
LOG_SEPERATOR = "\n"
308+
309+
class Log:
310+
def __init__(self, client, key):
311+
self.client = client
312+
self.key = key
313+
314+
def add(self, new_log):
315+
new_log += LOG_SEPERATOR
316+
self.client.append(self.key, new_log)
317+
318+
def get_all(self):
319+
all_logs = self.client.get(self.key)
320+
if all_logs is not None:
321+
log_list = all_logs.split(LOG_SEPERATOR)
322+
log_list.remove("") # 删除默认多余的空字符串
323+
return log_list
324+
else:
325+
return []
326+
```
327+
328+
329+
----------
330+
331+
下面介绍使用字符串键存储数字值:
332+
333+
每当存储一个值到字符串键里面的时候, 有下面两种情况
334+
1. C 语言 long long int 类型的整数, 取值范围为 -2^63 ~ 2^63-1 (超出范围会被当成字符串)
335+
2. C 语言 long double 类型的浮点数, 正规数公式为 `value = (-1)^s x (1 + fraction) x 2^(exp-bias)`, s:1位 为符号位, fraction:52位 为尾数[0, 1), exp:11位 指数位, bias:2^10-1=1023 偏移量; 对于次正规数(指数全0)公式为 `value = (-1)^s x fraction x 2^(1-bias)`
336+
- 最大值: 指数全1, 除了最高位(都为1是无穷大NaN), 尾数全1, `MAX = (1 + (1 - 2^(-52))) x 2^(2046-1023)`
337+
- 最小正规值: 指数最小正规化=1, 尾数全0, `Normalized_min = 1 x 2^(1-1023)`
338+
- 次正规数: 当指数全为0表示次正规数, 尾数最小=0...01, `Subnormal_min = 2^(-1022) x 2^(-52)`, 次正规数用来表示比最小正规数还小的正数, 但精度降低
102339

content/posts/2025-08-18_http-methods-and-status.md renamed to content/posts/2025-08-20_http-methods-and-status.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
+++
2-
date = '2025-08-18T8:00:00+08:00'
2+
date = '2025-08-20T8:00:00+08:00'
33
draft = true
44
title = 'HTTP Methods, Status Codes and Payloads'
55
tags = ["HTTP"]

0 commit comments

Comments
 (0)