Операции типа данных 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
реализация.