将范围-1到1的浮点数转换为短的快速方法?

Posted

技术标签:

【中文标题】将范围-1到1的浮点数转换为短的快速方法?【英文标题】:Fast way of converting float of range -1 to 1 to short? 【发布时间】:2010-08-12 09:26:59 【问题描述】:

我需要将 1024+ 个连续的 4 字节浮点数(范围 -1 到 1)重复转换为 2 字节短字节(范围 -32768 到 32767)并写入磁盘。

目前我使用循环来执行此操作:

short v = 0;
for (unsigned int sample = 0; sample < length; sample++) 

    v = (short)(inbuffer[sample * 2] * 32767.0f);
    fwrite(&v, 2, 1, file);

这可行,但浮点计算和循环很昂贵。有什么办法可以优化吗?

【问题讨论】:

警告:如果 -1.0 转换为 -32768 而 0.0 转换为 0,则 +1.0 应转换为 +32768,它不在 short 范围内。 也许 OP 的意思是“从 -1 到但不包括 1”。即便如此,浮点舍入错误也可能造成麻烦。也许最好将计算暂时保存在int 中,然后在将其存储在float 之前检查其值。 好点,-32767 到 32767 应该没问题。 这个问题是一个完美的例子,说明了为什么问题应该包含完整的上下文。 【参考方案1】:
short v = 0;
for (unsigned int sample = 0; sample < length; sample++) 

    v = (short)(inbuffer[sample * 2] * 32767.0f);
    // The problem is not here-------^^^^^^^^^^^
    fwrite(&v, 2, 1, file);        
    // it is here ^^^^^^^

典型的 Mac(objective-c 标签,或者我们在这里谈论的是 iphone?)每秒可以进行 十亿 次浮点乘法。然而 fwrite 是一个库调用,它遵循一些间接将其数据写入某个缓冲区并可能刷新它。最好批量填充自己的缓冲区:

short v[SZ] = 0;
// make sure SZ is always > length, or allocate a working buffer on the heap.
for (unsigned int sample = 0; sample < length; sample++) 

    v[sample] = (short)(inbuffer[sample * 2] * 32767.0f);

fwrite(v,sizeof(v),1,file);

【讨论】:

【参考方案2】:

我原以为重复调用fwrite 将是昂贵的部分。怎么样:

short outbuffer[length]; // note: you'll have to malloc this if length isn't constant and you're not using a version of C that supports dynamic arrays.
for (unsigned int sample = 0; sample < length; sample++) 

    outbuffer[sample] = (short)(inbuffer[sample * 2] * 32767.0f);

fwrite(outbuffer, sizeof *outbuffer, length, file);

【讨论】:

即使他有动态数组,在您不知道大小限制的上下文中使用它们也不是一个好主意。当心堆栈溢出。【参考方案3】:

我想,循环的瓶颈可能不是浮点转换,而是将输出写入文件 - 尝试将文件输出移到循环之外

short v = 0;
short outbuffer = // create outbuffer of required size
for (unsigned int sample = 0; sample < length; sample++) 

    outbuffer[sample] = (short)(inbuffer[sample * 2] * 32767.0f);


fwrite(outbuffer, 2, sizeof(outbuffer), file);

【讨论】:

【参考方案4】:

你可以试试这样的:

out[i] = table[((uint32_t *)in)[i]>>16];

其中table 是一个查找表,它将IEEE 浮点数的高16 位映射到您想要的int16_t 值。但是,这将失去一些精度。您需要保留并使用 23 位(1 个符号位、8 个指数位和 14 个尾数位)以获得全精度,这意味着一个 16 MB 的表,这会破坏缓存的一致性,从而降低性能。

您确定浮点转换很慢吗?只要您以这种方式使用fwrite,您在fwrite 上花费的CPU 时间是浮点运算的50-100 倍。如果您处理此问题并且代码仍然太慢,您可以使用添加魔术偏差并读取尾数位以转换为int16_t 的方法,而不是乘以 32767.0。这可能会也可能不会更快。

【讨论】:

以上是关于将范围-1到1的浮点数转换为短的快速方法?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 4 中的浮点数后打印 5 位数字,有没有最短的方法? [复制]

如何将小数字转换为python中的浮点数? [复制]

python中奇怪的浮点数到整数转换问题

c语言中如何将10进制的浮点数转化为16进制数

将 VarChar 转换为具有不同文化格式的浮点数

MODBUS RTU协议中浮点数是如何存储,读到浮点数寄存器的数值如何转换成所需的浮点数