Как преобразовать упакованное целое число (16.16) с фиксированной точкой в float?
Как преобразовать "32-разрядный подписанный номер фиксированной запятой (16.16)" в float?
Является ли (fixed >> 16) + (fixed & 0xffff) / 65536.0
ок? Как насчет -2,5? И -0.5?
Или fixed / 65536.0
правильный путь?
(PS: Как подписанная "-0,5" с фиксированной запятой в любом случае похожа на память?)
Ответы
Ответ 1
Я предполагаю два дополнительных 32-битных целых числа и операторы, работающие как на С#.
Как сделать преобразование?
fixed / 65536.0
является правильным и понятным.
(fixed >> 16) + (fixed & 0xffff) / 65536.0
Это эквивалентно приведенному выше для целых положительных чисел, но медленнее и труднее читать. Вы в основном используете дистрибутивный закон для разделения одного деления на два деления и записываете первый, используя бит-сдвиг.
Для отрицательных целых чисел fixed & 0xffff
не дает вам дробных битов, поэтому он не подходит для отрицательных чисел.
Посмотрите на необработанное целое число -1
, которое должно отображаться на -1/65536
. Этот код возвращает 65535/65536
.
В зависимости от вашего компилятора это может быть быстрее:
fixed * (1/65536.0)
Но я предполагаю, что большинство современных компиляторов уже делают эту оптимизацию.
Как все равно подписанная "-0,5" с фиксированной запятой выглядит в памяти?
Инвертирование преобразования дает нам:
RoundToInt(float*65536)
Настройка float=-0.5
дает нам: -32768
.
Ответ 2
class FixedPointUtils {
public static final int ONE = 0x10000;
/**
* Convert an array of floats to 16.16 fixed-point
* @param arr The array
* @return A newly allocated array of fixed-point values.
*/
public static int[] toFixed(float[] arr) {
int[] res = new int[arr.length];
toFixed(arr, res);
return res;
}
/**
* Convert a float to 16.16 fixed-point representation
* @param val The value to convert
* @return The resulting fixed-point representation
*/
public static int toFixed(float val) {
return (int)(val * 65536F);
}
/**
* Convert an array of floats to 16.16 fixed-point
* @param arr The array of floats
* @param storage The location to store the fixed-point values.
*/
public static void toFixed(float[] arr, int[] storage)
{
for (int i=0;i<storage.length;i++) {
storage[i] = toFixed(arr[i]);
}
}
/**
* Convert a 16.16 fixed-point value to floating point
* @param val The fixed-point value
* @return The equivalent floating-point value.
*/
public static float toFloat(int val) {
return ((float)val)/65536.0f;
}
/**
* Convert an array of 16.16 fixed-point values to floating point
* @param arr The array to convert
* @return A newly allocated array of floats.
*/
public static float[] toFloat(int[] arr) {
float[] res = new float[arr.length];
toFloat(arr, res);
return res;
}
/**
* Convert an array of 16.16 fixed-point values to floating point
* @param arr The array to convert
* @param storage Pre-allocated storage for the result.
*/
public static void toFloat(int[] arr, float[] storage)
{
for (int i=0;i<storage.length;i++) {
storage[i] = toFloat(arr[i]);
}
}
}
Ответ 3
После прочтения ответа CodesInChaos я написал шаблон функции С++, что очень удобно. Вы можете передать длину дробной части (например, формат файла BMP использует 2.30 номеров фиксированной точки). Если длина дробной части опущена, функция предполагает, что дробная и целая части имеют одинаковую длину
#include <math.h> // for NaN
#include <limits.h> // for CHAR_BIT = 8
template<class T> inline double fixed_point2double(const T& x, int frac_digits = (CHAR_BIT * sizeof(T)) / 2 )
{
if (frac_digits >= CHAR_BIT * sizeof(T)) return NAN;
return double(x) / double( T(1) << frac_digits) );
}
И если вы хотите прочитать такой номер из памяти, я написал шаблон функции
#include <math.h> // for NaN
#include <limits.h> // for CHAR_BIT = 8
template<class T> inline double read_little_endian_fixed_point(const unsigned char *x, int frac_digits = (CHAR_BIT * sizeof(T)) / 2)
// ! do not use for single byte types 'T'
{
if (frac_digits >= CHAR_BIT * sizeof(T)) return NAN;
T res = 0;
for (int i = 0, shift = 0; i < sizeof(T); ++i, shift += CHAR_BIT)
res |= ((T)x[i]) << shift;
return double(res) / double( T(1) << frac_digits) );
}