fixed-point 是一種小數表示方式,儲存固定位數小數。某個固定單位的部份,皆可視為小數。
閱讀時,有小數點分隔整數和小數。但在內部處理程式,並沒有實際的分隔,而由程式隱含定義位置。
低成本嵌入式處理器,沒有浮點運算單元,有些計算需要定點運算適當的 scaling 來達到可接受的動態範圍。定點運算可以比浮點運算更快、更高精確度,但寫程式需要更精確預測數值範圍,調整 scaling facotr,避免 overflow。
表示法
- Qm.n:通常是有號數,不含 sign-bit,整數部份 m-bit,小數部份 n-bit。在 ARM,m 包含 sign-bit。
- UQm.n:無號數,整數部份 m-bit,小數部份 n-bit。
- Qn:小數部份 n-bit。
加法和減法:相同 scaling factor,結果 scaling factor 不變。overflow。
乘法:scaling factor 相乘。在二進位,通常使用 scaling factor that is a power of two,乘完後 scaling factor 可透過 shifting right 調整回來。Rounding 可在 shift 前加上 scaling factor 的一半。證明:round(x/y) = floor(x/y + 0.5) = floor((x + y/2)/y) = shift-of-n(x + 2^(n-1))。
// saturate to range of int16_t
int16_t sat16(int32_t x)
{
if (x > 0x7FFF) return 0x7FFF;
else if (x < -0x8000) return -0x8000;
else return (int16_t)x;
}
int16_t q_mul(int16_t a, int16_t b)
{
int16_t result;
int32_t temp;
temp = (int32_t)a * (int32_t)b; // result type is operand's type
// Rounding; mid values are rounded up
temp += 1 << (Q - 1);
// Correct by dividing by base and saturate result
result = sat16(temp >> Q);
return result;
}
除法 https://lirobo.blogspot.com/2021/08/rounding-integer-division.html
int16_t q_div(int16_t a, int16_t b)
{
int32_t temp = (int32_t)a << Q;
// Rounding: mid values are rounded up.
if (((temp >> 31) & 1) == ((b >> 15) & 1)) {
temp += b>>1; // round up
} else {
temp -= b>>1; // round down for negative value
}
return (int16_t)(temp / b);
}
參考
- https://en.wikipedia.org/wiki/Fixed-point_arithmetic
- libfixmath