繁体   English   中英

C/C++ - 将 24 位有符号 integer 转换为浮点数

[英]C/C++ - Convert 24-bit signed integer to float

我在 C++ 中编程。我需要将 24 位签名的 integer(存储在 3 字节数组中)转换为浮点数(规范化为 [-1.0,1.0])。

该平台是 x86 上的 MSVC++(这意味着输入是小端)。

我试过这个:

float convert(const unsigned char* src)
{
    int i = src[2];
    i = (i << 8) | src[1];
    i = (i << 8) | src[0];

    const float Q = 2.0 / ((1 << 24) - 1.0);

    return (i + 0.5) * Q;
}

我不完全确定,但我从这段代码中得到的结果似乎不正确。 那么,我的代码错了吗?如果错了,为什么?

你没有签署将24位扩展为整数; 高位始终为零。 无论你的int大小是什么,这段代码都可以工作:

if (i & 0x800000)
    i |= ~0xffffff;

编辑:问题2是你的缩放常数。 简单来说,假设转换后0保持为0.0,您希望乘以新的最大值并除以旧的最大值。

const float Q = 1.0 / 0x7fffff;

最后,为什么在最终转换中添加0.5? 我能理解你是否想要舍入到一个整数值,但是你正朝着另一个方向前进。

编辑2:您指向的来源有一个非常详细的选择理由。 不是我选择的方式,但是完全可以防御。 我对乘数的建议仍然存在,但最大值因0.5增加因素而有所不同:

const float Q = 1.0 / (0x7fffff + 0.5);

因为添加后正负幅度相同,所以应该正确地缩放两个方向。

因为你正在使用一个char数组,所以输入因为是x86而不一定是小端。 char数组使字节顺序架构独立。

你的代码有点复杂。 一个简单的解决方案是移动24位数据以将其缩放到32位值(以便机器的自然签名算法可以工作),然后使用结果与最大可能值的简单比率(INT_MAX减去256因为空置的低8位)。

#include <limits.h>

float convert(const unsigned char* src)
{
    int i = src[2] << 24 | src[1] << 16 | src[0] << 8 ;
    return i / (float)(INT_MAX - 256) ;
}

测试代码:

unsigned char* makeS24( unsigned int i, unsigned char* s24 )
{
    s24[2] = (unsigned char)(i >> 16) ;
    s24[1] = (unsigned char)((i >> 8) & 0xff);
    s24[0] = (unsigned char)(i & 0xff);
    return s24 ;
}

#include <iostream>

int main()
{
    unsigned char s24[3] ;
    volatile int x = INT_MIN / 2 ;

    std::cout << convert( makeS24( 0x800000, s24 )) << std::endl ;  // -1.0
    std::cout << convert( makeS24( 0x7fffff, s24 )) << std::endl ;  //  1.0
    std::cout << convert( makeS24( 0, s24 )) << std::endl ;         //  0.0
    std::cout << convert( makeS24( 0xc00000, s24 )) << std::endl ;  // -0.5
    std::cout << convert( makeS24( 0x400000, s24 )) << std::endl ;  //  0.5

}

适合我的解决方案:

/**
 * Convert 24 byte that are saved into a char* and represent a float
 * in little endian format to a C float number.
 */
float convert(const unsigned char* src)
{
    float num_float;
    // concatenate the chars (short integers) and
    // save them to a long int
    long int num_integer = (
            ((src[2] & 0xFF) << 16) | 
            ((src[1] & 0xFF) << 8) | 
            (src[0] & 0xFF)
        ) & 0xFFFFFFFF;

    // copy the bits from the long int variable
    // to the float.
    memcpy(&num_float, &num_integer, 4);

    return num_float;
}

适合我:

float convert(const char* stream)
{
    int fromStream = 
        (0x00 << 24) + 
        (stream[2] << 16) + 
        (stream[1] << 8) + 
         stream[0];

    return (float)fromStream;
}

由于它不对称,这可能是最好的妥协。

映射 - ((2 ^ 23)-1)到-1.0和((2 ^ 23)-1)到1.0。

(注意:这与24位WAV文件使用的转换样式相同)

float convert( const unsigned char* src )
{
    int i = ( ( src[ 2 ] << 24 ) | ( src[ 1 ] << 16 ) | ( src[ 0 ] << 8 ) ) >> 8;
    return ( ( float ) i ) / 8388607.0;
}

看起来你将它视为一个24位无符号整数。 如果最高有效位为1,则需要通过将剩余的8位设置为1来使i负。

我不确定它是不是很好的编程习惯,但这似乎有效(至少在32位Linux上使用g ++,还没有尝试过其他任何东西)并且肯定比从一个字节逐个字节地提取更优雅一个char数组,特别是如果它不是一个char数组而是一个你读取的流(在我的例子中,它是一个文件流)(如果它一个char数组,你可以使用memcpy而不是istream::read )。

只需将24位变量加载到带符号的32位(带signed long )的不太重要的3个字节中。 然后将long变量向左移一个字节,使符号位出现在其意图所在的位置。 最后,只需规范化32位变量,就可以了。

union _24bit_LE{
  char access;
  signed long _long;
}_24bit_LE_buf;

float getnormalized24bitsample(){
  std::ifstream::read(&_24bit_LE_buf.access+1, 3);
  return (_24bit_LE_buf._long<<8) / (0x7fffffff + .5);
}

(奇怪的是,当你刚刚读入3个更重要的字节时,它似乎不起作用)。

编辑 :事实证明这种方法似乎有一些问题,我还没有完全理解。 最好不要暂时使用它。

这个,从这里得到,对我有用。

typedef union {
    struct {
        unsigned short lo;
        unsigned short hi;
    } u16;
    unsigned int u32;
    signed int i32;
    float  f;
}Versatype32;
//Bipolar version (-1.0 to ~1.0)
void fInt24_to_float(float* dest, const char* src, size_t length) {
    Versatype32 xTemp;
    while (length--) {
        xTemp.u32 = *(int*)src;
        //Check if Negative by right shifting 8
        xTemp.u32 <<= 8; //(If it's a negative, we'll know)  (Props to Norman Wong)
        //Convert to float
        xTemp.f = (float)xTemp.i32;     
        //Skip divide down if zero
        if (xTemp.u32 != 0) {
            //Divide by (1<<31 or 2^31)
            xTemp.u16.hi -= 0x0F80; //BAM! Bitmagic!
        }
        *dest = xTemp.f;
        //Move to next set
        dest++;
        src += 3;
    }   //Are we done yet?
    //Yes!
    return;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM