【学Rust写CAD】15 定点数实现(fixed.rs)
源代码
fixed.rs文件实现了一个定点数(Fixed Point)类型 Fixed,用于在整数运算中模拟小数运算。代码如下:
//小数位数
const FIXED_FRACTION_BITS: u32 = 16;
//用于 双线性插值(Bilinear Interpolation) 的计算,它决定了插值权重(weight)的精度位数。一般为4或8
const BILINEAR_INTERPOLATION_BITS: u32 = 4;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Fixed(i32);
impl Fixed {
pub const ONE: Fixed = Fixed(1 << FIXED_FRACTION_BITS);
pub const HALF: Fixed = Fixed(1 << (FIXED_FRACTION_BITS-1));//FIXED_ONE
/// 创建新的Fixed值
pub fn new(value: i32) -> Self {
Fixed(value)
}
/// 将浮点数转换为定点数
pub fn from_float(x: f32) -> Self {
Fixed(((x * (1 << FIXED_FRACTION_BITS) as f32) + 0.5) as i32)
}
/// 将定点数转换为整数(截断小数部分)
pub fn to_int(self) -> i32 {
self.0 >> FIXED_FRACTION_BITS
}
/// 获取双线性插值权重
pub fn bilinear_weight(self) -> u32 {
// 丢弃不需要的精度位
let reduced = self.0 >> (FIXED_FRACTION_BITS - BILINEAR_INTERPOLATION_BITS);
// 提取剩余的小数部分
let fraction = reduced & ((1 << BILINEAR_INTERPOLATION_BITS) - 1);
fraction as u32
}
/// 获取内部值
pub fn raw_value(self) -> i32 {
self.0
}
}
// 实现一些运算符重载以便更方便地使用
impl std::ops::Add for Fixed {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Fixed(self.0 + rhs.0)
}
}
impl std::ops::Sub for Fixed {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
Fixed(self.0 - rhs.0)
}
}
impl std::ops::Mul for Fixed {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
// 定点数乘法需要调整小数位
Fixed((self.0 as i64 * rhs.0 as i64 >> FIXED_FRACTION_BITS) as i32)
}
}
impl std::ops::Div for Fixed {
type Output = Self;
fn div(self, rhs: Self) -> Self {
// 定点数除法需要调整小数位
Fixed(((self.0 as i64 << FIXED_FRACTION_BITS) / rhs.0 as i64) as i32)
}
}
impl std::ops::Shr<u32> for Fixed {
type Output = Self;
fn shr(self, rhs: u32) -> Self {
Fixed(self.0 >> rhs)
}
}
impl std::ops::Shl<u32> for Fixed {
type Output = Self;
fn shl(self, rhs: u32) -> Self {
Fixed(self.0 << rhs)
}
}
代码解析:
常量定义
- FIXED_FRACTION_BITS: u32 = 16:
-
表示小数部分占用的位数
-
使用16位表示小数部分,意味着有16位小数和16位整数(在i32中)
- BILINEAR_INTERPOLATION_BITS: u32 = 4:
-
用于双线性插值计算的精度位数
-
通常设置为4或8位,决定了插值权重的精度
Fixed 结构体
Fixed 是一个包装了 i32 的新类型,用于表示定点数。
重要常量
-
ONE: 表示定点数1.0,值为 1 << 16 (65536)
-
HALF: 表示定点数0.5,值为 1 << 15 (32768)
主要方法
-
new(value: i32): 直接从一个i32值创建Fixed数
-
from_float(x: f32): 将浮点数转换为定点数
- 公式:x * (1 << 16) + 0.5 (0.5用于四舍五入)
- to_int(): 将定点数转换为整数(截断小数部分)
- 右移16位丢弃小数部分
- bilinear_weight(): 获取双线性插值权重
-
先丢弃不需要的精度位(保留BILINEAR_INTERPOLATION_BITS位)
-
然后提取剩余的小数部分作为权重
- raw_value(): 获取内部存储的原始i32值
运算符重载
实现了基本的算术运算,注意乘法和除法的特殊处理:
- 乘法:
-
需要先将操作数扩展为i64防止溢出
-
结果右移16位调整小数位
- 除法:
-
被除数左移16位扩展
-
然后进行除法运算
定点数表示原理
这个实现使用Q16.16格式的定点数:
-
32位整数(i32)中,高16位表示整数部分,低16位表示小数部分
-
例如:0x00010000 表示1.0 (1 << 16)
-
例如:0x00008000 表示0.5 (1 << 15)
应用场景
这种定点数实现常用于:
-
需要高性能小数运算但不想用浮点数的场合
-
图形处理中的坐标计算
-
嵌入式系统等不支持浮点运算硬件的环境
-
双线性插值等需要精确控制精度的算法
双线性插值中使用时,bilinear_weight()方法提供了权重计算,通过控制BILINEAR_INTERPOLATION_BITS可以调整插值精度。