IEEE754 одинарной точности - общий алгоритм для представления половины числа - PullRequest
0 голосов
/ 02 мая 2018

Предположим, что N - произвольное число, представленное в соответствии со стандартами одинарной точности IEEE754. Я хочу снова найти наиболее точное из возможных представлений N / 2 в IEEE754.

Я хочу найти общий алгоритм (описанный словами, я просто хочу, чтобы необходимые шаги и случаи учитывались) для получения представления.

Мой подход:

Скажите, что число представлено: b0_b1_b2_b3...b_34.

  1. Изолировать первый бит, который определяет знак (- / +) числа.
  2. Рассчитать представление степени (p) из беззнакового представления b_1...b_11.
  3. Если power = 128, у нас есть особый случай. Если все биты мантиссы равны 0, мы имеем, в зависимости от b_0, либо минус, либо плюс бесконечность. Мы ничего не меняем. Если у мантиссы есть хотя бы один бит, равный 1, то у нас есть значение NaN. Мы снова ничего не меняем.
  4. , если e is inside] - 126, 127 [then we have a normalized mantissa м. Новая мощность p can be calculated as p '= p - 1 and belongs in the interval] - 127, 126] . We then calculate m / 2`, и мы представляем ее, начиная справа и теряя любые биты, которые не могут быть включены в 23 бита мантисса.
  5. Если e = -126, то при вычислении половины этого числа мы передаем денормализованную мантиссу. Мы представляем p = 127, вычисляем половину мантиссы и представляем ее снова, начиная с права, теряя любую информацию, которая не может быть включена.
  6. Наконец, если e = -127, у нас есть денормализованная мантисса. Пока m/2 может быть представлено в количестве битов, доступных в мантиссе, без потери информации, мы представляем это и сохраняем p = -127. В любом другом случае мы представляем число в виде положительного или отрицательного 0 в зависимости от b_0

Какие-либо шаги, которые я пропустил, какие-либо улучшения (я уверен, что есть), которые могут быть сделаны, или что-то, что кажется совершенно неправильным?

1 Ответ

0 голосов
/ 03 мая 2018

Я реализовал алгоритм деления на два в Java и проверил его для всех 32-битных входных данных. Я пытался следовать вашему псевдокоду, но было три места, где я расходился. Во-первых, показатель бесконечности / NaN равен 128. Во-вторых, в случае 4 (нормальный -> нормальный) нет необходимости работать с дробью. В-третьих, вы не описали, как округляется половина до даже, когда вы работаете с дробью. LGTM в противном случае.

public final class FloatDivision {
  public static float divideFloatByTwo(float value) {
    int bits = Float.floatToIntBits(value);
    int sign = bits >>> 31;
    int biased_exponent = (bits >>> 23) & 0xff;
    int exponent = biased_exponent - 127;
    int fraction = bits & 0x7fffff;
    if (exponent == 128) {
      // value is NaN or infinity
    } else if (exponent == -126) {
      // value is normal, but result is subnormal
      biased_exponent = 0;
      fraction = divideNonNegativeIntByTwo(0x800000 | fraction);
    } else if (exponent == -127) {
      // value is subnormal or zero
      fraction = divideNonNegativeIntByTwo(fraction);
    } else {
      // value and result are normal
      biased_exponent--;
    }
    return Float.intBitsToFloat((sign << 31) | (biased_exponent << 23) | fraction);
  }

  private static int divideNonNegativeIntByTwo(int value) {
    // round half to even
    return (value >>> 1) + ((value >>> 1) & value & 1);
  }

  public static void main(String[] args) {
    int bits = Integer.MIN_VALUE;
    do {
      if (bits % 0x800000 == 0) {
        System.out.println(bits);
      }
      float value = Float.intBitsToFloat(bits);
      if (Float.floatToIntBits(divideFloatByTwo(value)) != Float.floatToIntBits(value / 2)) {
        System.err.println(bits);
        break;
      }
    } while (++bits != Integer.MIN_VALUE);
  }
}
...