Как преобразовать упакованное целое число (16.16) с фиксированной точкой в ​​число с плавающей точкой? - PullRequest
14 голосов
/ 27 декабря 2011

Как преобразовать 32-разрядное число с фиксированной запятой (16.16) в число с плавающей запятой?

В порядке (fixed >> 16) + (fixed & 0xffff) / 65536.0 хорошо? А как насчет -2,5? И -0,5?

Или fixed / 65536.0 правильный путь?

(PS: Как все-таки выглядит в памяти подписанная фиксированная точка "-0.5"?)

Ответы [ 3 ]

22 голосов
/ 27 декабря 2011

Я предполагаю, что два дополнительных 32-битных целых числа и операторы работают так же, как в C #.

Как сделать конвертацию?

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.

6 голосов
/ 09 июля 2012
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]);
    }
  }

}
0 голосов
/ 31 января 2018

После прочтения ответа CodesInChaos я написал шаблон функции C ++, что очень удобно.Вы можете передать длину дробной части (например, формат файла 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) );
}
...