C 浮点到定点转换

C Floating Point to Fixed Point conversion

提问人:jmsandiegoo 提问时间:2/21/2023 最后编辑:jmsandiegoo 更新时间:2/22/2023 访问量:440

问:

嗨,我正在尝试在不使用任何库函数的情况下在浮点和定点表示之间制作一个转换器。到目前为止,定点表示为 16 位,其中整数部分为 10,小数部分为 6。我一直在谷歌搜索并找到了可能的解决方案。

inline fixed_point_t double_to_fixed(double input)
{
    return (fixed_point_t)(input * (1 << FIXED_POINT_FRACTIONAL_BITS));
}

inline double fixed_to_double(fixed_point_t input)
{
    return ((double)input / (double)(1 << FIXED_POINT_FRACTIONAL_BITS));
}

我正在努力理解它背后的逻辑。有人可以帮忙解释一下吗?在这种情况下,小数位如何被 C 截断到只有 6 位 😅

c 二进制 定点

评论


答:

1赞 Support Ukraine 2/21/2023 #1

假设您想要一个使用 2 个小数位的定点类型。位模式

000...0000111  (7 decimal)

然后表示值

1 + 0.5 + 0.25 = 1.75

现在考虑浮点计算

7 / (1 << 2) = 7 / (2^2) = 7 / 4 = 1.75  (where ^ is power-of)

这正是它的工作原理。

您可以通过除以“2 的 NUMBER-OF-FRACTIONAL-BITS 的幂”从定点变量的全整数表示到浮点值

评论

0赞 jmsandiegoo 2/21/2023
感谢您的回复!你能帮忙解释一下double_to_fixed吗?当你有时间的时候
2赞 Fe2O3 2/21/2023 #2

这是怎么回事:

样本数 = 123.4567890(以 10 为基数)

仍然以 10 为基数,用“3 个定点小数”表示相同的数字。
要形成该值,请乘以 10^3 并去掉剩余的小数点。
123.456789 * 1000 =>整数 == 123457(或 123_457,其中“_”表示“固定小数点”。

这是转换为固定十进制的以 10 为基数的示例。
很明显,它可以通过除以 1000 来反转。(丢失的精度会丢失,无法恢复。

现在,不要以 10 为基数进行操作,而是使用以 2 为基数表示的浮点值 ( 或 ) 执行相同的操作。floatdouble

就是这么简单。这只是程度问题......

0赞 chux - Reinstate Monica 2/22/2023 #3

来理解其背后的逻辑。

大致如下:

fixed_point_t double_to_fixed(double input):乘以 26 并转换为整数。input

double fixed_to_double(fixed_point_t input):转换为 A 并除以 26double


找到了一个可能的解决方案。

以下有一些弱点:

inline fixed_point_t double_to_fixed(double input)
{
    return (fixed_point_t)(input * (1 << FIXED_POINT_FRACTIONAL_BITS));
}
  • “在这种情况下,小数位如何被 C 截断到只有 6 位” --> 是的,值小于 2-6 的位将被静默丢弃。考虑四舍五入input

  • 强制转换,某些整数类型,风险未定义的行为应该远远超出范围。(fixed_point_t)input(fixed_point_t)

  • 1 << FIXED_POINT_FRACTIONAL_BITS是 时开始的问题。对 .FIXED_POINT_FRACTIONAL_BITS > 151

  • 对于上面要处理的其他代码,也许可以删除 .inline

例:

#include <errno.h>
#include <stdint.h>

#define FIXED_POINT_FRACTIONAL_BITS 6
typedef int16_t fixed_point_t;
#define FIXED_POINT_I_MAX INT16_MAX
#define FIXED_POINT_I_MIN INT16_MIN
// Use the right width type for forming constants.
#define FIXED_POINT_C INT16_C

fixed_point_t double_to_fixed(double input) {
  input *= FIXED_POINT_C(1) << FIXED_POINT_FRACTIONAL_BITS;
  // Round ties away from zero by adding 0.5. (Other rounding modes possible.)
  // Works OK when the sum is exact.
  input += (input < 0) ? -0.5 : 0.5;

  // Avoid this form as FIXED_POINT_I_MAX + 1 may wrap
  // if (input >= (FIXED_POINT_I_MAX + 1)) {
  if (input >= (FIXED_POINT_I_MAX/2 + 1)*2.0) {
    errno = ERANGE;
    return FIXED_POINT_I_MAX;
  }

  // Avoid this form as FIXED_POINT_I_MIN - 1 as a FP may be inexact
  // if (input <= (FIXED_POINT_I_MIN - 1)) {
  if (input - FIXED_POINT_I_MIN <= -1) {
    errno = ERANGE;
    return FIXED_POINT_I_MIN;
  }

  return (fixed_point_t)input;
}

double fixed_to_double(fixed_point_t input)基本没问题。当不动点的精度(OP的情况下为6)超过(53)时,这是一个问题。double