Преобразование с плавающей точкой в ​​фиксированную точку - PullRequest
21 голосов
/ 09 октября 2008

В C ++, каков общий способ преобразования любого значения с плавающей запятой (float) в с фиксированной точкой (int, 16:16 или 24: 8)?

РЕДАКТИРОВАНИЕ: Для пояснения, значения с фиксированной запятой состоят из двух частей: целой части и дробной части. Целочисленная часть может быть представлена ​​целочисленным типом данных со знаком или без знака. Дробная часть представлена ​​целочисленным типом данных без знака.

Давайте сделаем аналогию с деньгами для ясности. Дробная часть может представлять центы - дробная часть доллара. Диапазон типа данных 'cents' будет от 0 до 99. Если 8-разрядное целое число без знака будет использоваться для математики с фиксированной запятой, то дробная часть будет разделена на 256 частей, равномерно делимых.

Надеюсь, это прояснит ситуацию.

Ответы [ 6 ]

28 голосов
/ 09 октября 2008

Вот, пожалуйста:

// A signed fixed-point 16:16 class
class FixedPoint_16_16
{
    short          intPart;
    unsigned short fracPart;

public:
    FixedPoint_16_16(double d)
    {
        *this = d; // calls operator=
    }

    FixedPoint_16_16& operator=(double d)
    {
        intPart = static_cast<short>(d);
        fracPart = static_cast<unsigned short>
                    (numeric_limits<unsigned short> + 1.0)*d);
        return *this;
    }

    // Other operators can be defined here
};

РЕДАКТИРОВАНИЕ: Вот более общий класс, основанный на другом обычном способе работы с числами с фиксированной запятой (и на который указал KPexEA):

template <class BaseType, size_t FracDigits>
class fixed_point
{
    const static BaseType factor = 1 << FracDigits;

    BaseType data;

public:
    fixed_point(double d)
    {
        *this = d; // calls operator=
    }

    fixed_point& operator=(double d)
    {
        data = static_cast<BaseType>(d*factor);
        return *this;
    }

    BaseType raw_data() const
    {
        return data;
    }

    // Other operators can be defined here
};


fixed_point<int, 8> fp1;           // Will be signed 24:8 (if int is 32-bits)
fixed_point<unsigned int, 16> fp1; // Will be unsigned 16:16 (if int is 32-bits)
21 голосов
/ 09 октября 2008

Приведение из числа с плавающей точкой в ​​целое число отбрасывает дробную часть, поэтому, если вы хотите сохранить эту дробь как фиксированную точку, вы просто умножаете число с плавающей точкой перед тем, как его разыграть. Приведенный ниже код не проверяет наличие переполнения.

Если хотите 16: 16

double f = 1.2345;
int n;

n=(int)(f*65536);

если хотите 24: 8

double f = 1.2345;
int n;

n=(int)(f*256);
7 голосов
/ 09 октября 2008

**** Редактировать **: Мой первый комментарий применяется до редактирования Кевина, но я оставлю его здесь для потомков. Иногда ответы меняются очень быстро!

Проблема с подходом Кевина состоит в том, что с Fixed Point вы обычно упаковываетесь в гарантированный размер слова (обычно 32 бита). Объявление двух частей по отдельности оставляет вас в прихоти структуры вашего компилятора. Да, вы могли бы форсировать это, но это не работает ни для чего, кроме представления 16:16.

KPexEA ближе к цели, упаковав все в int - хотя я бы использовал «sign long», чтобы попытаться быть явным на 32битах. Затем вы можете использовать его подход для генерации значения с фиксированной точкой, а разделение битов снова извлекает составные части. Его предложение также охватывает случай 24: 8.

(И все остальные, кто предложил просто static_cast ..... о чем ты думал?;))

1 голос
/ 31 октября 2008

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

В нем использовались шаблоны, и было легко избавиться от зависимостей от библиотеки lib.

0 голосов
/ 09 октября 2008

Нет встроенной поддержки в C ++ для чисел с фиксированной точкой. Лучше всего было бы написать класс-оболочку «FixedInt», который принимает двойные числа и конвертирует их.

Что касается универсального метода для преобразования ... часть int достаточно проста, просто возьмите целочисленную часть значения и сохраните ее в старших битах ... десятичная часть будет выглядеть примерно так:

for (int i = 1; i <= precision; i++)
{
   if (decimal_part > 1.f/(float)(i + 1)
   {
      decimal_part -= 1.f/(float)(i + 1);
      fixint_value |= (1 << precision - i);
   }
}

хотя это может содержать ошибки все еще

0 голосов
/ 09 октября 2008

Это хорошо для преобразования из числа с плавающей запятой в целое число, но О. также хотел фиксированная точка .

Теперь, как бы вы сделали это в C ++, я не знаю (C ++ - это не то, что я легко могу придумать). Возможно, попробуйте подход с целочисленным целочисленным значением, то есть используйте 32- или 64-разрядное целое число и программно выделите последние, скажем, 6 цифр для того, что находится справа от десятичной точки.

...