首页 > 解决方案 > 如何在 C 中将浮点数转换为 16 位有符号整数小数?

问题描述

我正在使用 NXP 为其 Arm M4F 提供的库中的宏将浮点类型变量转换为名为 frac16_t 的库数据类型,当浮点值小于 -1 时它不起作用。frac16_t 被 typedef 为有符号短。

宏代码为:

#define FRAC16(x) ((frac16_t)((x) < 0.999969482421875 ? ((x) >= -1 ? (x)*0x8000 : 0x8000) : 0x7fff))

该宏的预期行为是将 -1 和 +1 之间的任何浮点值转换为 16 位有符号整数,表示从 -1 到 (1 - 1/2^15) 的范围,其中 0x8000 作为 -1 值,0x7fff 和近1个值。如果浮点值大于(几乎)1,则结果在 0x7fff 处饱和,当浮点值小于 -1 时,结果应为 0x8000。

实际发生的是,对于任何小于 -1 的输入,结果都是 0x7fff(即接近 1),对于任何其他值,它都像宣传的那样工作。

我确实发现将 0x8000 常量转换为 frac16_t 类型可以使宏正常工作,但我不明白为什么原始库宏不起作用。将常量更改为 -32768 也有效,这两个修复程序都会导致常量被编码为 32 位长,这需要从加载指令附近的闪存中的某处间接加载该值,而不是作为 16 位文字加载,这是操作说明。

标签: floating-pointdata-conversionshort

解决方案


这两个修复都导致常量被编码为 32 位长,这需要从闪存中的某处间接加载该值

不完全的。十六进制常量被转换doublesigned short.

首先与test ? some_type_A : some_type_B,结果是一个常见的类型。在这种情况下,double

(x)*0x8000是a double(or float),然后: 0x8000: 0x7fff变成同样的浮点类型。

0x8000变成 32768.0。将超出范围分配doublesigned shortUB。
常见的 UB 是超出范围的值采用最小/最大限制。
在 OP 的情况下double,32768.0 变为signed short32767。

#define FRAC16(x) \\
    ((frac16_t)((x) < 0.999969482421875 ? ((x) >= -1 ? (x)*0x8000 : 0x8000) : 0x7fff))
//                                                                  ^^^^^^    
//                                                                  32768.0

与其将 32768.0 分配给 asigned short并调用undefined behavior,不如将 -32768.0 分配给已定义的行为。

#define FRAC16(x)
    ((frac16_t)((x) < 0.999969482421875 ? ((x) >= -1 ? (x)*0x8000 : -32768 : 0x7fff))
//                                                                  ^^^^^^^
//                                                                  -32768.0

如果想用 编码SHRT_MIN,不要使用0x8000,使用SHRT_MINor (-0x7fff - 1)


推荐阅读