Операции типа данных Python numpy float16 и float8?

при выполнении математических операций над числами Float16 Numpy результат также находится в номере типа float16. Мой вопрос в том, как именно вычисляется результат? Скажем, умножая / добавляя два числа float16, python генерирует результат в float32, а затем усекает/округляет результат до float16? Или расчет выполняется в "16-битном мультиплексоре/сумматоре" полностью?

другой вопрос - есть ли тип float8? Я не мог найти этого... если нет, то почему? Спасибо вам всем!

2 ответов


к первому вопросу: нет аппаратной поддержки для float16 на типичном процессоре (по крайней мере, за пределами GPU). NumPy делает именно то, что вы предлагаете: преобразовать float16 операнда float32, выполните скалярную операцию на float32 - значения, вокруг float32 результат float16. Можно доказать, что результаты по-прежнему правильно округлены: точность float32 достаточно большой (по отношению к float16) это двойное округление не является проблемой здесь, по крайней мере для четыре основные арифметические операции и квадратный корень.

в текущем источнике NumPy это то, как выглядит определение четырех основных арифметических операций для float16 операции скалярного.

#define half_ctype_add(a, b, outp) *(outp) = \
        npy_float_to_half(npy_half_to_float(a) + npy_half_to_float(b))
#define half_ctype_subtract(a, b, outp) *(outp) = \
        npy_float_to_half(npy_half_to_float(a) - npy_half_to_float(b))
#define half_ctype_multiply(a, b, outp) *(outp) = \
        npy_float_to_half(npy_half_to_float(a) * npy_half_to_float(b))
#define half_ctype_divide(a, b, outp) *(outp) = \
        npy_float_to_half(npy_half_to_float(a) / npy_half_to_float(b))

приведенный выше код взят из scalarmath.С. СРЦ в источнике NumPy. Вы также можете взглянуть на петли.С. СРЦ для соответствующего кода для массива ufuncs. Поддержка npy_half_to_float и npy_float_to_half функции определены в halffloat.c, наряду с различными другими функциями поддержки для float16 тип.

для второго вопроса: нет, нет float8 введите NumPy. float16 - стандартизированный тип (описанный в стандарте IEEE 754), который уже широко используется в некоторых контекстах (в частности, графических процессорах). Нет IEEE 754 float8 тип, и, похоже, нет очевидного кандидата на "стандартный"float8 тип. Я бы также предположил, что просто не было такого большого спроса на float8 поддержка в NumPy.


этот ответ основывается на float8 аспект вопроса. Принятый ответ охватывает остальное довольно хорошо.Одна из главных причин, почему нет широко принятого float8 тип, кроме отсутствия стандарта является то, что его не очень полезным практически.

праймер на плавающей точке

в стандартной нотации, a float[n] тип данных сохраняется с помощью n биты в памяти. Это означает, что самое большее 2^n уникальные значения могут быть представлены. В IEEE 754, a несколько из этих возможных значений, таких как nan, даже не цифры как таковые. Это означает, что все представления с плавающей запятой (даже если вы идете float256) имеют пробелы в множество рациональных чисел, которые они могут представлять и их округление до ближайшего значения, если вы пытаетесь получить представление числа в этом промежутке. Как правило, чем выше n, чем меньше эти зазоры.

вы можете увидеть разрыв в действии, если вы используете struct пакет для получения двоичного представления некоторых float32 цифры. Его немного поразительно, чтобы столкнуться сначала, но есть разрыв 32 только в целочисленном пространстве:

import struct

billion_as_float32 = struct.pack('f', 1000000000 + i)
for i in range(32):
    billion_as_float32 == struct.pack('f', 1000000001 + i) // True

как правило, с плавающей запятой лучше всего отслеживать только самые значительные биты, так что если ваши номера имеют одинаковый масштаб, важные различия сохраняются. Стандарты с плавающей запятой обычно отличаются только тем, как они распределяют доступные биты между базой и показателем. Например, IEEE 754 float32 использует 24 бита для основания и 8 бит для экспоненты.

на float8

по приведенной выше логике, a float8 значение может принимать только 256 различных значений, независимо от того, насколько вы умны в разделении битов между базой и показателем. Если вы не заинтересованы в округлении чисел до одного из 256 произвольных чисел, кластеризованных около нуля, вероятно, более эффективно отслеживать 256 возможностей в int8.

например, если вы хотите отслеживать очень маленький диапазон грубая точность вы можете разделить диапазон на 256 точек, а затем сохранить, какой из 256 точек ваш номер был ближе всего. Если вы хотите по-настоящему фантазировать, вы можете иметь нелинейное распределение значений, сгруппированных в центре или по краям, в зависимости от того, что для вас важнее всего.

вероятность того, что кто-то еще (или даже вы позже) нуждается в этой точной схеме очень маленький и большую часть времени дополнительный байт или 3 вы платите в качестве наказания за использование float16 или float32 вместо этого слишком мало, чтобы добиться существенных результатов. Следовательно...почти никто не потрудился написать float8 реализация.