Domanda

Sono la programmazione in C ++. Ho bisogno di convertire un intero con segno a 24 bit (memorizzato in una matrice 3-byte) a galleggiante (normalizzazione a [-1.0,1.0]).

La piattaforma è MSVC ++ su x86 (che significa che l'ingresso è little endian).

Ho provato questo:

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

Non sono del tutto sicuro, ma sembra i risultati che sto ottenendo da questo codice non sono corrette. Quindi, è il mio codice errato e, se sì, perché?

È stato utile?

Soluzione

Non sono firmare estendere i 24 bit in un intero; i bit superiori saranno sempre zero. Questo codice funziona, non importa quale sia la dimensione int è:

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

Modifica Problema 2 è il vostro costante ridimensionamento. In termini semplici, si vuole moltiplicare per il nuovo massimo e dividere per il vecchio massima, partendo dal presupposto che resti 0 a 0.0 dopo la conversione.

const float Q = 1.0 / 0x7fffff;

Infine, perché stai aggiungendo 0,5 nella conversione finale? Potrei capire se si stava tentando di tutto per un valore intero, ma si sta andando nella direzione opposta.

Modifica 2: La sorgente si punta ha una logica molto dettagliato per le vostre scelte. Non è il modo in cui mi avrebbe scelto, ma perfettamente difendibile comunque. Il mio consiglio per il moltiplicatore detiene ancora, ma il massimo è diverso a causa del fattore di 0.5 ha aggiunto:

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

Poiché le grandezze positive e negative sono le stesse dopo l'aggiunta, questo dovrebbe scala correttamente entrambe le direzioni.

Altri suggerimenti

Poiché si utilizza un array di caratteri, ma non necessariamente che l'ingresso è little endian in virtù di essere x86; char rende l'architettura ordine byte indipendente.

Il codice è un po 'più di complicato. Una soluzione semplice è quella di spostare i dati a 24 bit in scala in un valore a 32 bit (in modo che naturali potrà aritmetica firmata della macchina), e quindi utilizzare un semplice rapporto tra il risultato con il valore massimo possibile (che è INT_MAX meno 256 perché dei vuoti inferiori 8 bit).

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

Codice di prova:

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

}

Dato che non è simmetrica, questo è probabilmente il miglior compromesso.

Mappe -. ((2 ^ 23) -1) -1.0 e ((2 ^ 23) -1) a 1.0

(Nota: questo è lo stesso stile di conversione utilizzato dai file WAV 24 bit)

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

La soluzione che funziona per me:

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

funziona per me:

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

    return (float)fromStream;
}

Sembra che tu sia trattandolo come un numero intero senza segno a 24 bit. Se il bit più significativo è 1, è necessario effettuare i negativo impostando i restanti 8 bit a 1 pure.

Non sono sicuro se è buona pratica di programmazione, ma questo sembra funzionare (almeno con g ++ su Linux a 32 bit, non sono altro ancora provato su qualsiasi cosa) ed è certamente più elegante di estrazione di byte per -byte da un array di caratteri, soprattutto se non è davvero un array di caratteri, ma piuttosto un flusso (nel mio caso, si tratta di un flusso di file) che si legge da (se è un array di caratteri, si può uso memcpy invece di istream::read).

Basta caricare la variabile 24 bit nei meno significativi 3 byte di un segno a 32 bit (signed long). Poi spostare la variabile di un byte long a sinistra, in modo che il bit di segno appare in cui è destinata ad. Infine, proprio normalizzare la variabile a 32 bit, e sei a posto.

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

(Stranamente, non sembra al lavoro quando hai appena letto nelle 3 byte più significativi subito).

Modifica : si scopre questo metodo sembra avere alcuni problemi che non capiscono fino in fondo ancora. Meglio non ne fanno uso per il momento.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top