Как разобрать число с фиксированной точкой, дополняющее два, из примитивного целочисленного типа с произвольным разбиением целого числа и дроби? - PullRequest
0 голосов
/ 24 декабря 2018

Я работаю над анализом файлов шрифтов OpenType, и мне нужно проанализировать (и записать) два вида чисел с фиксированной запятой:

  • 16-битное фиксированное число со знаком с младшими 14 битами дроби(2.14)
  • 32-разрядное число с фиксированной точкой со знаком (16.16)

Я предполагаю, что в конце оно должно быть приведено к / из f32


Спецификация OpenType описывает:

Формат F2DOT14 состоит из целого числа со знаком 2, дополнения 2 и дроби без знака.Чтобы вычислить фактическое значение, возьмите целое число и добавьте дробь.

Примеры значений 2.14:

Decimal Value   Hex Value   Integer     Fraction
1.999939        0x7fff      1           16383/16384
1.75            0x7000      1           12288/16384
0.000061        0x0001      0           1/16384
0.0             0x0000      0           0/16384
-0.000061       0xffff      -1          16383/16384
-2.0            0x8000      -2          0/16384

У меня есть решение, которое работает, но только для2.14 значения:

fn from(number: u16) -> f32 {
    let mut int = (number >> 14) as f32;
    if int > 1f32 {
        int -= 4f32;
    }
    let frac = (number & 0b11_1111_1111_1111) as f32 / 16384 as f32;
    int + frac
}

Поскольку целочисленное значение должно быть [-2, 2), я вычитаю 4, если проанализированное целое число больше 1, чтобы получить отрицательные числа.

Яищем способ сделать это для любого возможного разделения чисел с фиксированной точкой (например, 2.14, 16.16, 3.5, 24.40 и т. д.) внутри стандартного диапазона целочисленных примитивных типов Rust (* 1034)*, u32, u64 и т. Д.).

1 Ответ

0 голосов
/ 24 декабря 2018

Я смог решить мою проблему, вот пример разбора 16-битного числа с фиксированной точкой:

use std::mem::size_of;

fn from_u16(raw: u16, frac_count: usize) -> f32 {
  let bit_count = size_of::<u16>() * 8;
  let int_count = bit_count - frac_count;

  let unsigned = (raw >> frac_count) as isize;
  let sign_bit = unsigned >> (int_count - 1) & 1;
  let high_bits = if sign_bit == 1 { -1 } else { 0 };
  let signed = high_bits << int_count | unsigned as isize;

  let mut mask = 0;
  for i in 0..=frac_count {
    mask = mask << i | 1;
  }

  let frac = (raw & mask) as f32 / (1 << frac_count) as f32;
  signed as f32 + frac
}
...