【发布时间】:2021-11-21 18:24:19
【问题描述】:
我正在尝试制作一个小型定点数学库。我的定点数是 32 位的,整数和小数部分各有 16 位。麻烦在于添加定点数然后查看结果值。下面的函数fixed_from_parts 采用整数和小数部分,并发出一个定点数,所以fixed_from_parts(5, 2) 等于0000000000000101.0000000000000010。
当两个数相加时,如下面main函数所示,似乎整数部分作为一个数字相加,而小数部分作为另一个数字相加(5.2 + 3.9 错误地变成了 8.11,因为 5 + 3 == 8 和 2 + 9 == 11)。我认为我需要反转存储在小数部分中的位的顺序,但我不太确定该怎么做。我是否过于复杂了?如何使加法正常工作?
#include <stdint.h>
#include <stdio.h>
typedef int16_t integral_t;
typedef int32_t fixed_t;
fixed_t int_to_fixed(const integral_t x) {
return x << 16;
}
integral_t fixed_to_int(const fixed_t x) {
return x >> 16;
}
// shifts right (clears integral bits), and then shifts back
integral_t get_fixed_fractional(const fixed_t x) {
return (integral_t) x << 16 >> 16;
}
// fixed_from_parts(5, 2) == 5.2
fixed_t fixed_from_parts(const integral_t integral, const integral_t fractional) {
return int_to_fixed(integral) + fractional;
}
void print_fixed_base_2(const fixed_t x) {
for (int i = (sizeof(fixed_t) << 3) - 1; i >= 0; i--) {
putchar((x & (1 << i)) ? '1' : '0');
if (i == sizeof(fixed_t) << 2) putchar('.');
}
putchar('\n');
}
void print_fixed_base_10(const fixed_t x) {
printf("%d.%d\n", fixed_to_int(x), get_fixed_fractional(x));
}
int main(void) {
// 5.2 + 3.9 = 9.1
const fixed_t a = fixed_from_parts(5, 2), b = fixed_from_parts(3, 9);
print_fixed_base_2(a);
print_fixed_base_2(b);
const fixed_t result = a + b;
print_fixed_base_2(result);
print_fixed_base_10(result); // why is the result 8.11?
}
【问题讨论】:
-
您需要了解定点的工作原理。让我们使用更少的位,比如 8 位,其中 4 位用于整数,4 位用于小数。那么
0101.0010的数字不是 5.2。它是5 + 2/16,十进制是5.125。位权重(假设无符号数)为8, 4, 2, 1, 0.5, 0.25, 0.125, 0.0625。所以0101.0010是4 + 1 + 0.125 = 5.125。 -
fixed_from_parts(5, 2)不是5.2,而是5 + 2/(2^16) -
您需要更加小心地定义定点格式的实际工作方式。将 5.1 存储为 0x00050001 可能会起作用,但让我们想一想:如果 5.1 是 0x00050001,这是否意味着 5.10 是 0x0005000a?但是5.1应该和5.10一样! 5.11呢?是 0x0005000b 还是别的什么?那么 5.01 或 5.001 呢?
-
16 位可以轻松存储多达 9999,因此您可能想说分数是千分之一。所以 5.1 和 5.10 都是
(5 << 16) | 1000,你可以将 5.0001 表示为 0x50001。 -
但另一件事是,如果你这样做,在做加法之后,你将不得不手动实现一个 carry 从小数部分到不可分割的一部分。没有进位导致你得到错误的答案:你有 5.2 + 3.9 错误地输出为 8.11,因为如你所见,2+9=11。相加后需要查看和,如果对应大于1的分数,则在整数部分实现进位。
标签: c decimal fixed-point