C/C++ - Преобразуйте 24-разрядное целое число со знаком для плавания

Я программирую в C++. Я должен преобразовать 24-разрядное целое число со знаком (сохраненный в 3 массивах байтов) для плавания (нормализующий к [-1.0 1.0]).

Платформой является MSVC ++ на x86 (что означает, что вход является прямым порядком байтов).

Я попробовал это:

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

Я не совсем уверен, но это кажется результатами, которые я получаю из этого кода, являются неправильными. Так, мой код неправильно и если так, почему?

6
задан Etienne Dechamps 26 May 2010 в 19:47
поделиться

3 ответа

Вы не используете знак, расширяющий 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);

Поскольку положительная и отрицательная величины одинаковы после сложения, это должно правильно масштабироваться в обоих направлениях.

11
ответ дан 8 December 2019 в 12:58
поделиться

Похоже, что вы рассматриваете его как 24-битное беззнаковое целое. Если старший бит равен 1, вам нужно сделать i отрицательным, установив остальные 8 бит также в 1.

0
ответ дан 8 December 2019 в 12:58
поделиться

Поскольку вы используете массив символов, из этого не следует, что входные данные являются little endian в силу того, что они x86; массив символов делает порядок байтов независимым от архитектуры.

Ваш код несколько переусложнен. Простым решением является сдвиг 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) ;
}

Test code:

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

}
4
ответ дан 8 December 2019 в 12:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: