目錄
一、原理介紹? ? ? ??
二、代碼實現
三、結果顯示
四、移植到C語言中的應用
4.1. 定義定點數配置和參數
4.2. 實現分段查找函數
4.3. 實現 log10 近似計算函數
4.4. (可選)定點數轉浮點數
一、原理介紹? ? ? ??
????????之前的博文對數函數分段線性實現講解了理論方法和誤差分析。這篇博文講解其定點實現方法。
二、代碼實現
import math# 定點數配置
FRACTIONAL_BITS = 16 # 小數部分位數
INTEGER_BITS = 32 - FRACTIONAL_BITS # 整數部分位數
SCALE_FACTOR = 1 << FRACTIONAL_BITS # 2^16 = 65536def generate_segment_points():"""生成所有分段點"""points = [0, 1, 2, 3, 4, 5, 6, 7]# 后續以2的指數為點,直到不超過4095exp = 3 # 2^3 = 8,接續7之后while True:point = 2 ** expif point > 4095:breakpoints.append(point)exp += 1# 確保最后一個點是4095if points[-1] < 4095:points.append(4095)return pointsdef float_to_fixed(value):"""將浮點數轉換為定點數表示"""# 計算最大值和最小值max_val = (1 << INTEGER_BITS) - (1.0 / SCALE_FACTOR)min_val = -(1 << INTEGER_BITS)# 截斷到范圍內if value > max_val:value = max_valelif value < min_val:value = min_val# 轉換為定點數fixed_point = int(round(value * SCALE_FACTOR))# 確保在32位有符號整數范圍內if fixed_point > 0x7FFFFFFF:fixed_point = 0x7FFFFFFFelif fixed_point < -0x80000000:fixed_point = -0x80000000return fixed_pointdef fixed_to_float(fixed_value):"""將定點數轉換回浮點數"""return fixed_value / SCALE_FACTORdef calculate_segments(points):"""計算每段的斜率和截距(浮點和定點)"""segments = []for i in range(len(points) - 1):x1 = points[i]x2 = points[i + 1]# 處理x=0的特殊情況(log10(0)無定義,用log10(1)代替)if x1 == 0:y1 = math.log10(1) # log10(1) = 0else:y1 = math.log10(x1)y2 = math.log10(x2)# 計算斜率和截距(浮點)if x2 == x1:k_float = 0.0else:k_float = (y2 - y1) / (x2 - x1)b_float = y1 - k_float * x1# 轉換為定點數k_fixed = float_to_fixed(k_float)b_fixed = float_to_fixed(b_float)# 還原定點數為浮點數,用于對比k_fixed_float = fixed_to_float(k_fixed)b_fixed_float = fixed_to_float(b_fixed)segments.append({'segment_id': i,'start': x1,'end': x2,# 浮點參數'k_float': k_float,'b_float': b_float,# 定點參數(整數表示)'k_fixed': k_fixed,'b_fixed': b_fixed,# 定點參數還原為浮點(用于精度參考)'k_fixed_float': k_fixed_float,'b_fixed_float': b_fixed_float})return segmentsdef main():# 生成分段點points = generate_segment_points()print(f"分段點: {points}")print(f"共{len(points)-1}段直線\n")# 計算分段參數segments = calculate_segments(points)# 輸出定點數配置print("定點數配置:")print(f"總位數: 32位")print(f"整數部分: {INTEGER_BITS}位")print(f"小數部分: {FRACTIONAL_BITS}位")print(f"縮放因子: 2^{FRACTIONAL_BITS} = {SCALE_FACTOR}\n")# 顯示斜率對比print("斜率(k)參數對比:")print(f"{'分段ID':<8} {'x范圍':<12} {'浮點值':<18} {'定點整數值':<18} {'定點還原浮點值':<18}")print("-" * 80)for seg in segments:x_range = f"[{seg['start']}, {seg['end']}]"print(f"{seg['segment_id']:<8} {x_range:<12} {seg['k_float']:<18.10f} "f"{seg['k_fixed']:<18} {seg['k_fixed_float']:<18.10f}")# 顯示截距對比print("\n截距(b)參數對比:")print(f"{'分段ID':<8} {'x范圍':<12} {'浮點值':<18} {'定點整數值':<18} {'定點還原浮點值':<18}")print("-" * 80)for seg in segments:x_range = f"[{seg['start']}, {seg['end']}]"print(f"{seg['segment_id']:<8} {x_range:<12} {seg['b_float']:<18.10f} "f"{seg['b_fixed']:<18} {seg['b_fixed_float']:<18.10f}")# 生成嵌入式可用的C語言數組定義print("\n嵌入式C語言參數數組:")print("/* 分段點定義 */")print(f"const uint16_t segment_points[] = {{ {', '.join(map(str, points))} }};")print(f"const uint8_t num_segments = {len(segments)};\n")print("/* 斜率(k)定點數值組 */")k_values = [str(seg['k_fixed']) for seg in segments]print(f"const int32_t k_fixed[] = {{ {', '.join(k_values)} }};\n")print("/* 截距(b)定點數值組 */")b_values = [str(seg['b_fixed']) for seg in segments]print(f"const int32_t b_fixed[] = {{ {', '.join(b_values)} }};")if __name__ == "__main__":main()
三、結果顯示
段點: [0, 1, 2, 3, 4, 5, 6, 7, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4095]
共17段直線定點數配置:
總位數: 32位
整數部分: 16位
小數部分: 16位
縮放因子: 2^16 = 65536斜率(k)參數對比:
分段ID x范圍 浮點值 定點整數值 定點還原浮點值
--------------------------------------------------------------------------------
0 [0, 1] 0.0000000000 0 0.0000000000
1 [1, 2] 0.3010299957 19728 0.3010253906
2 [2, 3] 0.1760912591 11540 0.1760864258
3 [3, 4] 0.1249387366 8188 0.1249389648
4 [4, 5] 0.0969100130 6351 0.0969085693
5 [5, 6] 0.0791812460 5189 0.0791778564
6 [6, 7] 0.0669467896 4387 0.0669403076
7 [7, 8] 0.0579919470 3801 0.0579986572
8 [8, 16] 0.0376287495 2466 0.0376281738
9 [16, 32] 0.0188143747 1233 0.0188140869
10 [32, 64] 0.0094071874 617 0.0094146729
11 [64, 128] 0.0047035937 308 0.0046997070
12 [128, 256] 0.0023517968 154 0.0023498535
13 [256, 512] 0.0011758984 77 0.0011749268
14 [512, 1024] 0.0005879492 39 0.0005950928
15 [1024, 2048] 0.0002939746 19 0.0002899170
16 [2048, 4095] 0.0001470073 10 0.0001525879 截距(b)參數對比:
分段ID x范圍 浮點值 定點整數值 定點還原浮點值
--------------------------------------------------------------------------------
0 [0, 1] 0.0000000000 0 0.0000000000
1 [1, 2] -0.3010299957 -19728 -0.3010253906
2 [2, 3] -0.0511525224 -3352 -0.0511474609
3 [3, 4] 0.1023050449 6705 0.1023101807
4 [4, 5] 0.2144199393 14052 0.2144165039
5 [5, 6] 0.3030637741 19862 0.3030700684
6 [6, 7] 0.3764705126 24672 0.3764648438
7 [7, 8] 0.4391544112 28780 0.4391479492
8 [8, 16] 0.6020599913 39457 0.6020660400
9 [16, 32] 0.9030899870 59185 0.9030914307
10 [32, 64] 1.2041199827 78913 1.2041168213
11 [64, 128] 1.5051499783 98642 1.5051574707
12 [128, 256] 1.8061799740 118370 1.8061828613
13 [256, 512] 2.1072099696 138098 2.1072082520
14 [512, 1024] 2.4082399653 157826 2.4082336426
15 [1024, 2048] 2.7092699610 177555 2.7092742920
16 [2048, 4095] 3.0102589912 197280 3.0102539062 嵌入式C語言參數數組:
/* 分段點定義 */
const uint16_t segment_points[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4095 };
const uint8_t num_segments = 17;/* 斜率(k)定點數值組 */
const int32_t k_fixed[] = { 0, 19728, 11540, 8188, 6351, 5189, 4387, 3801, 2466, 1233, 617, 308, 154, 77, 39, 19, 10 };/* 截距(b)定點數值組 */
const int32_t b_fixed[] = { 0, -19728, -3352, 6705, 14052, 19862, 24672, 28780, 39457, 59185, 78913, 98642, 118370, 138098, 157826, 177555, 197280 };
四、移植到C語言中的應用
4.1. 定義定點數配置和參數
首先在 C 文件中定義定點數配置(需與 Python 中一致)和從 Python 復制的參數:
#include <stdint.h> // 用于uint16_t、int32_t等類型// 定點數配置(必須與Python代碼中的設置一致)
#define FRACTIONAL_BITS 16 // 小數部分位數(與Python的FRACTIONAL_BITS相同)
#define SCALE_FACTOR (1 << FRACTIONAL_BITS) // 2^16 = 65536// 從Python輸出復制的分段參數
const uint16_t segment_points[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4095};
const uint8_t num_segments = sizeof(segment_points) / sizeof(segment_points[0]) - 1;const int32_t k_fixed[] = {28961, // 第0段斜率(示例值,需替換為Python輸出)13230, // 第1段斜率// ... 其他分段的k值146 // 最后一段斜率
};const int32_t b_fixed[] = {0, // 第0段截距(示例值,需替換為Python輸出)-15731, // 第1段截距// ... 其他分段的b值196602 // 最后一段截距
};
4.2. 實現分段查找函數
根據輸入 x(0~4095),找到對應的分段索引(即使用哪組 k 和 b):
/*** 查找x所屬的分段索引* 參數:x - 輸入值(0~4095)* 返回:分段索引(0 ~ num_segments-1)*/
static uint8_t find_segment(uint16_t x) {// 遍歷分段點,找到x所在的區間for (uint8_t i = 0; i < num_segments; i++) {if (x >= segment_points[i] && x <= segment_points[i + 1]) {return i;}}return num_segments - 1; // 兜底(x=4095)
}
4.3. 實現 log10 近似計算函數
利用找到的分段參數(k 和 b),通過定點數運算計算 log10 (x) 的近似值:
/*** 計算log10(x)的近似值(定點數輸出)* 參數:x - 輸入值(0~4095,整數)* 返回:log10(x)的定點數表示(精度:FRACTIONAL_BITS位小數)*/
int32_t log10_approx(uint16_t x) {if (x == 0) {return 0; // 特殊處理x=0(log10(0)無定義,返回0)}// 1. 查找x所屬的分段uint8_t seg_idx = find_segment(x); // 或使用find_segment_fast// 2. 取出該分段的k和b(定點數)int32_t k = k_fixed[seg_idx];int32_t b = b_fixed[seg_idx];// 3. 定點數運算:y = k * x + b(需處理小數位對齊)// x是整數,先轉換為定點數(x << FRACTIONAL_BITS)int64_t x_fixed = (int64_t)x << FRACTIONAL_BITS; // 擴展為64位避免乘法溢出// 計算k * x:結果小數位為FRACTIONAL_BITS*2,右移FRACTIONAL_BITS位對齊int64_t product = (k * x_fixed) >> FRACTIONAL_BITS;// 加上截距b(已為定點數,直接相加)int64_t y_fixed = product + b;// 4. 截斷為32位(防止溢出)if (y_fixed > INT32_MAX) {return INT32_MAX;} else if (y_fixed < INT32_MIN) {return INT32_MIN;} else {return (int32_t)y_fixed;}
}
4.4. (可選)定點數轉浮點數
若需要將結果轉換為浮點數(如用于顯示或上層計算):
/*** 將定點數結果轉換為浮點數* 參數:fixed_val - log10_approx返回的定點數* 返回:對應的浮點數*/
float fixed_to_float(int32_t fixed_val) {return (float)fixed_val / SCALE_FACTOR;
}