Преобразование с плавающей запятой в фиксированную точку

Каков общий способ преобразования любого значения с плавающей запятой (float) в с фиксированной точкой < / strong> (целое, 16:16 или 24: 8)?

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

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

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


person CVertex    schedule 09.10.2008    source источник
comment
Если вы работаете в Visual C ++, поэкспериментируйте с переключателем /fp:fast, прежде чем даже думать о преобразовании всей вашей плавающей точки в фиксированную. Этот переключатель модели с плавающей запятой допускает оптимизацию, которая может позволить плавающей запятой легко превзойти фиксированную точку по скорости. Определенно недооцененная функция.   -  person Special Sauce    schedule 30.12.2015


Ответы (6)


Ну вот:

// 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)
person Kevin    schedule 09.10.2008
comment
Это больше о том, как анализировать число с плавающей запятой, а не преобразовывать его в представление с фиксированной запятой. - person Trap; 31.03.2009
comment
Что же здесь такое DataType? Где это определяется? - person dicroce; 21.12.2011
comment
Кроме того, как будет реализовано преобразование другим способом (из фиксированной точки в двойное). - person dicroce; 21.12.2011

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

Если хочешь 16:16

double f = 1.2345;
int n;

n=(int)(f*65536);

если хочешь 24: 8

double f = 1.2345;
int n;

n=(int)(f*256);
person KPexEA    schedule 09.10.2008

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

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

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

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

person Greg Whitfield    schedule 09.10.2008

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

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

person CVertex    schedule 31.10.2008
comment
ссылка не работает (404) - person Ivan Black; 29.10.2016
comment
В следующий раз лучше связать связанный с этим вопрос. Как пояснил выше меня Иван, комментируя, вопросы остаются актуальными. Этот ответ больше не содержит никакой информации. - person Jake Millington; 02.02.2017
comment
Обновлена ​​ссылка на копию archive.org на дату публикации. - person mskfisher; 22.08.2018

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

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

person bugmagnet    schedule 09.10.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);
   }
}

хотя он, вероятно, все еще содержит ошибки

person workmad3    schedule 09.10.2008