标题不言自明:如何将 IEEE-11073 16 位 SFLOAT 转换为 Java 中的简单浮点数?
7 回答
IEEE-11073 不在公共领域,但您可以在蓝牙个人健康档案中找到足够的信息。谷歌完整规范#11073-2060。以下是蓝牙个人健康转码纸的复制粘贴:
以下信息在 ISO/IEEE 标准中定义。11073-2060™1-2008 [1]。SFLOAT-Type 数据类型被定义为表示非整数类型的数值。SFLOAT-Type 定义为具有 12 位尾数和 4 位指数的 16 位值。有关 SFLOAT 类型的详细定义,请参见 [1] 的附录 F.8。该数据类型定义如下: 指数尾数大小 4 位 12 位
16位浮点型;整数类型只是占位符
SFLOAT-Type ::= INT-U16 16 位值包含以 10 为底的 4 位指数,后跟 12 位尾数。每个都是二进制补码形式。分配特殊值以表示以下内容: NaN [指数 0,尾数 +(2^11 –1) → 0x07FF] NRes [指数 0,尾数 –(2^11) → 0x0800] + INFINITY [指数 0,尾数 +( 2^11 –2) → 0x07FE] – INFINITY [指数 0,尾数 –(2^11 –2) → 0x0802] 保留供将来使用 [指数 0,尾数 –(2^11 –1) → 0x0801]
这个 11073 库具有执行此操作的 C 代码:
https://github.com/signove/antidote/blob/master/src/util/bytelib.c
转换成 Java 应该不难。
double read_sfloat(ByteStreamReader *stream, int *error)
{
intu16 int_data = read_intu16(stream, error);
if (*error)
return 0;
intu16 mantissa = int_data & 0x0FFF;
int8 expoent = int_data >> 12;
if (expoent >= 0x0008) {
expoent = -((0x000F + 1) - expoent);
}
float output = 0;
if (mantissa >= FIRST_S_RESERVED_VALUE && mantissa
<= MDER_S_NEGATIVE_INFINITY) {
output = reserved_float_values[mantissa
- FIRST_S_RESERVED_VALUE];
} else {
if (mantissa >= 0x0800) {
mantissa = -((0x0FFF + 1) - mantissa);
}
double magnitude = pow(10.0f, expoent);
output = (mantissa * magnitude);
}
return output;
}
样板代码:
typedef enum {
MDER_S_POSITIVE_INFINITY = 0x07FE,
MDER_S_NaN = 0x07FF,
MDER_S_NRes = 0x0800,
MDER_S_RESERVED_VALUE = 0x0801,
MDER_S_NEGATIVE_INFINITY = 0x0802
} ReservedSFloatValues;
static const intu32 FIRST_S_RESERVED_VALUE = MDER_S_POSITIVE_INFINITY;
intu16 read_intu16(ByteStreamReader *stream, int *error)
{
intu16 ret = 0;
if (stream && stream->unread_bytes > 1) {
ret = ntohs(*((uint16_t *) stream->buffer_cur));
stream->buffer_cur += 2;
stream->unread_bytes -= 2;
} else {
if (error) {
*error = 1;
}
ERROR("read_intu16");
}
return ret;
}
您可以使用位移。提取符号、指数和尾数并将它们移位,使它们为浮点格式。您可能需要更正 Infinity 和 NaN。
正如@PretiP 的回答所指出的那样,指数以 10 为底,因此您需要乘以或除以 10 的幂才能得到最终值。
尝试搜索“Personal Health Devices Transcoding_WP_V11”,它将引导您找到来自蓝牙特别兴趣小组的文档。在 2011 年 10 月 25 日/V11r00 版本的文档中,第 2.2 节“将蓝牙特性转码为 11073 属性”给出了如何处理 11073-20601 FLOAT(32 位)和 SFLOAT(16 位)数字的详细说明和示例。
本文档的当前 URL 为 https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=242961
请注意,这可能与 Petri P. 上面引用的文件相同。
即使这篇文章有点旧,我只想发布我的解决方案,基于这个java 文件。
public short getExponent(short value)
{
if (value < 0)
{ // if exponent should be negative
return (byte) (((value >> 12) & 0x0F) | 0xF0);
}
return (short) ((value >> 12) & 0x0F);
}
public short getMantissa(short value)
{
if ((value & 0x0800) != 0)
{ // if mantissa should be negative
return (short) ((value & 0x0FFF) | 0xF000);
}
return (short) (value & 0x0FFF);
}
public double parseSFLOATtoDouble(short value)
{
// NaN
if (value == 0x07FF)
{
return Double.NaN;
}
// NRes (not at this resolution)
else if (value == 0x0800)
{
return Double.NaN;
}
// +INF
else if (value == 0x07FE)
{
return Double.POSITIVE_INFINITY;
}
// -INF
else if (value == 0x0802)
{
return Double.NEGATIVE_INFINITY;
}
// Reserved
else if (value == 0x0801)
{
return Double.NaN;
}
else
{
return ((double) getMantissa(value)) * Math.pow(10, getExponent(value));
}
}
对于那些希望在使用 Javascript 时转换 SFLOAT 的人来说,这里有一个简洁的库,可以做到这一点,等等。 https://github.com/bluetoother/ble-packet
如果你想添加自己的特性,只需使用 addMeta 函数,defs/charMeta.js 有很多例子!
我找不到任何与 IEEE 11073 相关的浮点规范,您可能是指半精度浮点(有时也称为 Minifloat)。
该格式在 Wikipedia 中有充分的描述,可以轻松地将其转换为普通浮点数。基本上,您将其分为 3 个字段(符号、指数、尾数)。标志不需要转换,只需要移动到正确的位置。然后检查指数是否为 MIN 或 MAX 值,处理特殊情况(Inf、NaN、次正规/非正规化)。否则,只需纠正指数的偏差并转移到正确的位置。对于尾数,根据需要在右侧添加尽可能多的零。最后将所有内容放在一个 int 中,并使用 Float.intBitsToFloat(bits) 将这些位转换为普通的 java 浮点数。
从浮点数转换的工作方式几乎相同,只是存在舍入、上溢和下溢的额外陷阱。