online number hexadecimal gregstoll float convertir bytes arithmetic c++ c floating-point integer 24bit

number - C/C++-Convertir entero de 24 bits con signo para flotar



gregstoll float to hex (7)

Estoy programando en C ++. Necesito convertir un entero con signo de 24 bits (almacenado en una matriz de 3 bytes) para flotar (normalizando a [-1.0,1.0]).

La plataforma es MSVC ++ en x86 (lo que significa que la entrada es little-endian).

Intenté esto:

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; }

No estoy del todo seguro, pero parece que los resultados que obtengo de este código son incorrectos. Entonces, ¿mi código está equivocado? Si es así, ¿por qué?


No estoy seguro si es una buena práctica de programación, pero parece funcionar (al menos con g ++ en Linux de 32 bits, no lo he probado en otra cosa) y es ciertamente más elegante que extraer byte a byte de una matriz char, especialmente si no es realmente una matriz char, sino más bien una secuencia (en mi caso, es una secuencia de archivos) de la que lees (si es una matriz char, puedes usar memcpy lugar de istream::read ).

Simplemente cargue la variable de 24 bits en los 3 bytes menos significativos de un signo de 32 bits (con signed long ). A continuación, cambie la variable long un byte a la izquierda, de modo que el bit de signo aparezca donde debe. Finalmente, simplemente normalice la variable de 32 bits, y ya está todo listo.

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); }

(Extrañamente, no parece funcionar cuando solo lees los 3 bytes más significativos de inmediato).

EDITAR : resulta que este método parece tener algunos problemas que aún no entiendo completamente. Mejor no lo use por el momento.


Como está utilizando una matriz de caracteres, no necesariamente se deduce que la entrada sea poco endian por ser x86; la matriz char hace que la arquitectura de orden de bytes sea independiente.

Tu código es algo complicado. Una solución simple es cambiar los datos de 24 bits para escalarlos a un valor de 32 bits (para que funcione la aritmética natural firmada de la máquina), y luego usar una relación simple del resultado con el valor máximo posible (que es INT_MAX menos 256 porque de los 8 bits inferiores vacantes).

#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) ; }

Código de prueba:

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 }


No eres signo extendiendo los 24 bits en un entero; los bits superiores siempre serán cero. Este código funcionará sin importar tu tamaño int :

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

Editar: El problema 2 es la constante de escala. En términos simples, quiere multiplicar por el nuevo máximo y dividir por el máximo anterior, suponiendo que 0 permanece en 0.0 después de la conversión.

const float Q = 1.0 / 0x7fffff;

Finalmente, ¿por qué agregas 0.5 en la conversión final? Podría entender si estabas tratando de redondear a un valor entero, pero vas en la otra dirección.

Edición 2: la fuente a la que apunta tiene una justificación muy detallada para sus elecciones. No de la manera que habría elegido, pero perfectamente defendible de todos modos. Mi consejo para el multiplicador aún se mantiene, pero el máximo es diferente debido al factor agregado de 0.5:

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

Debido a que las magnitudes positivas y negativas son las mismas después de la suma, esto debería escalar ambas direcciones correctamente.


Parece que lo está tratando como un entero sin signo de 24 bits. Si el bit más significativo es 1, necesita hacer negativo también ajustando los 8 bits restantes a 1.


Como no es simétrico, este es probablemente el mejor compromiso.

Mapas - ((2 ^ 23) -1) a -1.0 y ((2 ^ 23) -1) a 1.0.

(Nota: este es el mismo estilo de conversión utilizado por los archivos WAV de 24 bits)

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


La solución que funciona para mí:

/** * 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; }


Funciona para mi:

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