Как python округляет число float32 после умножения? - PullRequest
1 голос
/ 03 июня 2019

Я проектирую аппаратный множитель с плавающей запятой одинарной точности и использую python для проверки моей конструкции. Это формат с плавающей запятой одинарной точности.

с | показатель | мантисса

Умноженный результат -

(S1 ^ S2) | E1 + E2 - 127 | Мантисса1 * Мантисса2

У меня проблема с округлением числа после выполнения умножения двух мантисс. Вы знаете, мантисса составляет 24 бита (23 и 1 скрытый бит), и умножение двух 24 бит даст 48 бит. Поле мантиссы может содержать только 23 бита, поэтому я попытался обрезать его следующим образом: пример усечения мантиссы . Но результат не выглядит правильным по сравнению с умножением на python float32.
Так что я здесь, чтобы спросить, как python делает с умножением мантиссы. Спасибо.

Ответы [ 3 ]

2 голосов
/ 03 июня 2019

Python (и многие другие языки) используют режим округления по умолчанию IEEE754, известный как «Округление до ближайшей привязки к четности». Подробнее об этом вы можете прочитать здесь: https://en.wikipedia.org/wiki/IEEE_754#Rounding_rules

1 голос
/ 03 июня 2019

В этом ответе я объясню, как выполнить округление, используя правило IEEE-754 округления до ближайшего связывания с чеком для базового 32-разрядного двоичного двоичного числа с плавающей точкой.Это скорее всего то, что вы хотите.Хотя во многих реализациях Python используется IEEE-754, этого не требуется в документации Python, и даже в реализациях, использующих IEEE-754, могут быть отклонения от стандарта IEEE-754, поэтому не следует полагаться на Python как наавторитетная ссылка.

Учитывая 48-битное значение и 1 , давайте нумеруем биты от 0 для старшего значащего бита до 47 для младшего значащего бита.(Это обратное направление от обычного направления, но это удобно для этого обсуждения.)

Если произведение находится в пределах нормального диапазона формата с плавающей запятой, значения будут округлены:

  • Рассмотрим бит 24 и завершающие биты после этого.Если бит 24 равен 0, округлите (просто используйте биты от 0 до 23).Если бит 24 равен 1, а любой завершающий бит равен 1, округлите его в большую сторону (возьмите от 0 до 23 и добавьте к ним один).Если бит 24 равен 1, а все завершающие биты равны 0, округлить в меньшую сторону, если бит 23 четный (ноль), и округлить в большую сторону, если бит 23 нечетный (один).
  • При округлении вверх это может привести к переполнению битов 0до 23 (если они были 1111… 111, сложение выполняется из бита 0).В этом случае сдвиньте их вправо на один бит и добавьте один к показателю степени.

Как указано выше, выше для нормальных случаев.Для обработки всех случаев:

  • Проверка на ноль: Если значение и равно нулю, вернуть ноль (с вычисленным знаком).
  • НормализоватьЗначение: Если бит 0 поля значимого и равен 0, сдвиньте значение и оставьте один бит и вычтите один из показателя степени.Повторяйте этот шаг до тех пор, пока бит 0 значимого и не станет 0.
  • Тест на переполнение: Если истинный показатель превышает 127 (смещенный показатель превышает 254), выведите бесконечность (с вычисленным знаком).
  • Ограничение показателя : Если истинный показатель (который я назову e ) меньше -126 (смещенный показатель меньше 1), сдвиньтеЗначим и вправо на -126− e бит, вставляя слева 0.Установите показатель степени на -126.Округлите значениеи, как указано выше, с битами, пронумерованными от 0 до 47 + (- 126− e ).
  • Проверка переполнения: Если округление в большую сторону увеличило показатель степенидо 127 - бесконечность (с вычисленным знаком).
  • - получение нормального результата: Если бит 0 значимого и равен 1, используйте биты с 1 по 23 для поля значимого и e + 127 для поля экспоненты.
  • Произведите субнормальный результат: В противном случае бит 0 значения и равен 0. В этом случае все еще используются биты от 1 до 23 для значения иполе, но используйте ноль для поля экспоненты.Это поле экспоненты указывает субнормальное число.

Обратите внимание, что шаг «ограничить экспоненту» может казаться отменяющим шаг «нормализовать значимость», делая первый ненужным, но эта комбинация обрабатывает все случаинормальные и субнормальные операнды, дающие нормальные и субнормальные результаты.

Сноска

1 «Значение» - это термин, используемый в стандарте IEEE-754 для дробной части плавающего числаномер точки.«Мантисса» является старым термином для дробной части логарифма.(Значения линейные. Мантиссы логарифмические.)

0 голосов
/ 04 июня 2019

В прошлом я выполнял эту задачу в качестве упражнения для приложения работы:

Скрипт Python для генерации случайных чисел с плавающей точкой и создания тестового вектора значений IEEE754 и продукта:

import bitstring, random 

spread = 10000000
n = 100000

def ieee754(flt):
    b = bitstring.BitArray(float=flt, length=32)
    return b

if __name__ == "__main__":
    with open("TestVector", "w") as f:
        for i in range(n):
            a = ieee754(random.uniform(-spread, spread))
            b = ieee754(random.uniform(-spread, spread))

            # calculate desired product based on 32-bit ieee 754 
            # representation of the floating point of each operand
            ab = ieee754(a.float * b.float)

            f.write(a.bin + "_" + b.bin + "_" + ab.bin + "\n")

Verilog-код для 32-разрядного умножения с плавающей запятой с округлением до ближайшего четного:

/*   Input and outputs are in single-precision IEEE 754 FP format:
     Sign     -   1 bit, position 32
     Exponent -  8 bits, position 31-24
     Mantissa - 23 bits, position 22-1

Not implemented: special cases like inf, NaN
*/

// indices of components of IEEE 754 FP
`define SIGN    31
`define EXP     30:23
`define M       22:0

`define P       24    // number of bits for mantissa (including 
`define G       23    // guard bit index
`define R       22    // round bit index
`define S       21:0  // sticky bits range

`define BIAS    127


module fp_mul(input  wire clk,
              input  wire[31:0] a,
              input  wire[31:0] b,
              output wire[31:0] y);

    reg [`P-2:0] m;
    reg [7:0] e;
    reg s;

    reg [`P*2-1:0] product;
    reg G;
    reg R;
    reg S;
    reg normalized;

    reg state;
    reg next_state = 0;

    parameter STEP_1 = 1'b0, STEP_2 = 1'b1;


    always @(posedge clk) begin
        state <= next_state;
    end


    always @(state) begin

        case(state)
            STEP_1: begin
                // mantissa is product of a and b's mantissas, 
                // with a 1 added as the MSB to each
                product = {1'b1, a[`M]} * {1'b1, b[`M]};

                // get sticky bits by ORing together all bits right of R
                S = |product[`S]; 

                // if the MSB of the resulting product is 0
                // normalize by shifting right    
                normalized = product[47];
                if(!normalized) product = product << 1; 

                next_state = STEP_2;            
                end 

            STEP_2: begin
                // if either mantissa is 0, result is 0 
                if(!a[`M] | !b[`M]) begin 
                    s = 0; e = 0; m = 0;
                end else begin
                    // sign is xor of signs
                    s = a[`SIGN] ^ b[`SIGN];

                    // mantissa is upper 22-bits of product w/ nearest-even rounding
                    m = product[46:24] + (product[`G] & (product[`R] | S));

                    // exponent is sum of a and b's exponents, minus the bias 
                    // if the mantissa was shifted, increment the exponent to balance it
                    e = a[`EXP] + b[`EXP] - `BIAS + normalized;
                end 

                next_state = STEP_1;
                end
        endcase
    end

    // output is concatenation of sign, exponent, and mantissa  
    assign y = {s, e, m};
endmodule 

Надеюсь, это поможет!

...