Преобразование с плавающей точкой в неподвижную точку
В С++, какой общий способ конвертировать любое значение с плавающей запятой (float) в фиксированная точка (int, 16:16 или 24: 8)?
EDIT: Для уточнения значения фиксированной точки имеют две части: целую часть и дробную часть. Целочисленная часть может быть представлена целым типом данных с подписью или без знака. Дробная часть представлена целым типом данных без знака.
Давайте сделаем аналогию с деньгами ради ясности. Дробная часть может представлять центы - дробную часть доллара. Диапазон значений типа "центов" будет от 0 до 99. Если для математики с фиксированной точкой должно использоваться 8-разрядное целое число без знака, то дробная часть будет разделена на 256 равномерно разделяемых частей.
Я надеюсь, что это прояснит ситуацию.
Ответы
Ответ 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);
}
}
хотя это, вероятно, будет содержать ошибки еще