Подтвердить что ты не робот

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

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

EDIT: Для уточнения значения фиксированной точки имеют две части: целую часть и дробную часть. Целочисленная часть может быть представлена ​​целым типом данных с подписью или без знака. Дробная часть представлена ​​целым типом данных без знака.

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

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

4b9b3361

Ответ 1

Здесь вы идете:

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

EDIT:. Здесь более общий класс, основанный на другом обычном способе работы с номерами фиксированной точки (и который указал 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)

Ответ 2

Отбрасывание от float до integer будет выбрасывать дробную часть, поэтому, если вы хотите сохранить эту фракцию в качестве фиксированной точки, вы просто умножаете float перед ее литьем. В приведенном ниже коде не будет проверяться, что вы переполняете вас.

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

double f = 1.2345;
int n;

n=(int)(f*65536);

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

double f = 1.2345;
int n;

n=(int)(f*256);

Ответ 3

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

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

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

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

Ответ 4

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

Он использовал шаблоны и легко переключал зависимости на boost lib.

Ответ 5

Это отлично подходит для преобразования с плавающей запятой в integer, но O.P. также хотел фиксированную точку.

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

Ответ 6

В С++ нет встроенной поддержки для чисел с фиксированной точкой. Лучше всего было бы написать класс "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);
   }
}

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