2017-01-18 11 views
2

Мне нужно преобразовать float в Q31 с фиксированной точкой, значение Q31 означает 1 знаковый бит, 0 бит для целочисленной части и 31 бит для дробной части. Это означает, что Q31 может представлять только числа в диапазоне [-1,0.9999].Картирование [-1, + 1] плавает до Q31 фиксированной точки

По определению, при переходе от поплавка к фиксированной точке, умножение на 2ˇN делается, где N является дробной частью размера, в данном случае 31.

Однако я запуталась с этим кодом, то не работает, но работает:

#define q31_float_to_int(x) ((int) ((float)(x)*(float)0x7FFFFFFF)) 

И, похоже, все в порядке. Например:

int a = q31_float_to_int(0.5f); 

дает Hex: 0x40000000, что все в порядке.

Почему множитель здесь используется с 2ˇ31 - 1, а не только 2ˇ31?

+1

'(с плавающей точкой) 0x7FFFFFFF' является' 2147483648.00000': HTTP: //ideone.com/mawlXx. Даже после кастинга до 'unsigned' значение имеет значение: http://ideone.com/7WMeRE – mch

+0

Хмм? Как получилось 0x7FFFFFFF заканчивается как 2147483648, а не 2147483647 ?? – Danijel

+3

Потому что 2147483647 не может быть представлен как 'float', и поэтому будет найдено ближайшее число, которое равно 2147483648. – mch

ответ

1

Этот код не является хорошим решением для конвертации с плавающей запятой в неподвижную точку. Я предполагаю, что кто бы ни написал код, использовал масштабный коэффициент 0x7FFFFFFF, чтобы избежать переполнения, когда вход 1.0. Правильный масштабный коэффициент составляет 2^31, а не 2^31 - 1. Обратите внимание, что при преобразовании float (с 24 битами точности) также возникают проблемы точности до Q1.31 (с 31 бит точности). Рассмотрим насыщением входные данные перед умножением:

const float Q31_MAX_F = 0x0.FFFFFFp0F; 
const float Q31_MIN_F = -1.0F; 
float clamped = fmaxf(fminf(input, Q31_MAX_F), Q31_MIN_F); 

Код выше будет зажимать input в диапазоне [-1.0, 1.0). Константа Q31_MAX_F составляет приблизительно 1 - (2^-24), учитывая 24-битную точность и Q31_MIN_F - -1. Тогда вы можете умножить clamped на 2^31, или даже лучше, используйте scalbnf или ldexpf:

int result = (int) scalbnf(clamped, 31); 

И если вы хотите округление:

int result = (int) roundf(scalbnf(clamped, 31))); 
+0

Зачем использовать '0x7FFFFF00.p-31F;', а не самый большой 'float' под 1? '(0x7FFFFF80.p-31F;)' Еще лучше переносить: 'Q31_MAX_F = nextafterf (1.0,0.0); Q31_MIN_F = -1.0f; ' – chux

+0

Для _round_, предложите' int32_t result = (int32_t) lround (scalbnf (clamped, 31)); ' – chux

+0

Спасибо. Обновлено в соответствии с вашими комментариями. –