Как стать автором
Обновить

Posit-арифметика: победа над floating point на его собственном поле. Часть 1

Время на прочтение12 мин
Количество просмотров14K
Всего голосов 30: ↑29 и ↓1+28
Комментарии22

Комментарии 22

Жаль только то, что на внедрение нового стандарта в железо потребуется несколько лет. Компиляторы я думаю быстрее будут готовы.

Замечательная тема перевода!


Но читать тяжело, описание местами непонятное. Например, нигде не даётся определение числу es, и поэтому что такое useed (useed = 2^(2^es)), тоже не сразу понятно.
Наверно, описание нужно было начать с кратких определений unum Type I / unum Type II / posit / valid, и сразу привести ту формулу "sign(p) * useed^k * 2^e * f".


А из этой формулы сразу видно, что формат, в принципе, похож на "обычные" форматы чисел с плавающей точкой, но имеет как бы 2 экспоненты, тем самым значительно расширяя диапазон.


И тут возникает вопрос, почему автор формата выбрал для записи расширения экспоненты фактически унарную запись (00..01, 1111...10), а не более компактный и удобный железу бинарный код. Неужели унарная запись компактнее?

Я согласен с вами, что в статье не хватает последовательных определений. Но я перевёл как есть, не переделывая текст.
По поводу унарной записи: да, здесь это необходимо, потому что мы не знаем заранее, сколько разрядов у нас будет занимать режим. Поэтому он кодируется таким способом, который позволяет однозначно идентифицировать его конец.

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

Безусловно.

Ну значит можно ужать это поле с унарной формы до бинарной?

Нет, нельзя.

Ужать так просто нельзя, нужно же как-то понять, где кончается порядок и начинается мантисса.
Можно записывать порядок кодированием Элиаса, но для порядков из IEEE floats он оказывается короче записи с фиксированным числом битов только когда порядок очень близок к 0. Правда, с некоторой потерей точности относительно IEEE можно записывать экстремально большие и экстремально маленькие порядки (http://helper.ipam.ucla.edu/publications/bdcws2/bdcws2_15049.pdf, слайды 16-17).

А я правильно понял, что все эти манипуляции с режимом нужны для того, чтобы упаковать первые 4 бита экспоненты в 3 бита?

Нет, здесь экспонента имеет переменный размер, который и задаётся режимом.

Он задается режимом или режим следует из того, что первые несколько бит могут быть нулями или единицами? Я так понимаю, мы делаем как бы run length encoding, чтобы чуть расширить динамический диапазон чисел? Мне кажется, там какая-то простая идея, которую можно просто объяснить, но сначала нужно понять :)
Как бы режим кодирует с помощью такого кодирования количество бит экспоненты. Таким образом мы получаем небольшой выигрыш в точности, когда экспонента близка к нулю, т.е. само число близко к 1. Для этого узкого диапазона в середине точность posit выше, чём float. Но дело в том, что для очень больших или очень малых чисел показатель экспоненты будет большим, число бит режима тоже, и на мантиссу останется меньше бит, чем у float. В этом недостаток формата posit.
> Одна из причин, по которой IEEE float не даёт идентичных результатов на различных системах является то, что для элементарный функций, таких, как log(x) и exp(x), по стандарту IEEE не требуется точности до последнего бита для любого возможного входа.

Интересно, давно отменили? :sarcasm:
В версии 2008 требуется (пункт 9.1), а более новых пока не выпускали.
(Не надо вспоминать промежуточное более длинное хранение в x87 и аналогах: во-первых, оно, наоборот, точнее, во-вторых — не стандарт.)
Вот такие вот «хитрые» диверсии убивают всё доверие к авторам.

Из этой статьи всё равно наполовину ничерта не понятно!!!


Что за бред написан вот в этой фразе
"режим означает масштабирующий фактор, равный useed^k, где useed = 2^2^es"
При том, что величина es не определена!
Вот вам некая таблица, вот и делайте с ней, что хотите.


Можно же было СРАЗУ сказать, что формат позит определяется ДВУМЯ величинами


  • общее количество разрядов
  • количество разрядов экспоненты

Итого, 1 разряд на знак, es на экспоненту, а (разрядность-1-es) — на убер-множитель "режим" и дробную часть?


Какой вообще смысл в 3-битных или 5-битных позитах?


Авторы что-то гениальное знают, тащатся с этого, а остальные трахайтесь как хотите.

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

Привет всем, кто прокрутил комментарии до конца ;-)

1. На правах заинтересованного читателя, для удобства публики добавлю сюда две ссылки на "продолжения" этой статьи от @Refridgerator

Испытания Posit по-взрослому

Испытания Posit по-взрослому. Спектральный анализ

2. В комментариях к этим "продолжениям" активно обсуждался вопрос о пользе posit-вычислений для физиков/математиков. Так вот, как физик, добавлю к этим обсуждениям свои пять копеек:

В формате posit нет “NaN” (нечисел), вместо этого, вычисления прерываются (...)
Если программист видит необходимость в использовании значений NaN, это показывает, что программа пока не завершена (...)

Как физик, скажу, что при работе с ЭКСПЕРИМЕНТАЛЬНЫМИ временными рядами (= любой долговременный мониторинг) такая концепция категорически неприемлема. У нас NaN - это вовсе не ошибка вычислений, а совершенно легальное значение входных данных. Да, часто такие значения как-то интерполируют (заполняют) перед проведением вычислений... но с методологической точки зрения это крайне сомнительный подход. Понятно, что иногда (например, при вычислении БПФ) без интерполяции пропусков обойтись невозможно. Но огромное множество алгоритмов вполне допускает возможность работы с рядами, содержащими NaN. Поэтому при работе с данными незачем их заполнять без крайней нужды! Везде, где этом возможно, надо работать только с реальными наблюдениями. Избегая искусственного придумывания всяких вымышленных значений в тех случаях, когда наблюдение почему-то не проводилось...

Например, если моменты возникновения NaN случайны, то та же АКФ вычисляется на ура без всякой интерполяции. После чего можно и спектр оценить. Правда, оба этих вычисления будут тогда стоить O(n*n), а не O(n*log(n))... но в спектральном анализе любой "сыр" всегда платный :-(

3. И про хранение данных. У нас очень часто при наблюдениях встречаются значения с относительно низкой точностью, полученные с 16-битного АЦП. Или с 24-битного, но где не все разряды задействованы (ну или в младших битах нет ничего, кроме шума). По физическому смыслу это real-значения... но отводить на их хранение 32 бита нет смысла. Впервые эта задачка у меня возникла еще в 1986 году. Я тогда сидел высоко горах и делал систему хранения и анализа экспериментальных временных рядов сначала на СМ-4 (она же PDP-11), а затем на первых IBM PC. И на обоих машинах у нас не было других real, кроме real*4. В общем, я тогда сделал линейное отображение нужного диапазона real на 16-битные целые, которые и хранились в БД. Диапазоны данных у нас были фиксированы, и точности вполне хватало.

А недавно у меня возникла задача упаковать еще менее точные real в минимум бит. Причем, несмотря на очень большую погрешность этих real, там периодически попадались экстремальные выбросы (например, при землетрясениях), которые тоже надо было хранить, а не выбраковывать. В общем, я стал смотреть варианты и наткнулся на 8-битный posit. Который практически сразу же был отвергнут по той простой причине, что для нас критически важны NaN - значения, а их там попросту нет. Ну и еще у нас обычно пакуются сразу тройки таких чисел (три компоненты), которые потом удобно запихнуть в 32-битное целое, поэтому вместо 8 бит можно использовать 10. В итоге я сделал

свой формат компактного хранения real

Если кому-то интересен исходный код, его можно найти вот тут, файл CHARLIB.FOR, функцииPACK_R_TO_INT(R)и UNPACK_R_FROM_INT(I) (функции пакуют real-число в диапазоне от 1 до 140000 в целую переменную в диапазоне 1..1024 и обратно).

Кстати, та самая (из 1986г!) упаковка real в 16-бит у нас тоже до сих пор крутится в продакте. См. там же файл ABD_DB.FOR.

В каком-то смысле это тоже аналог posit-формата. Только в нашем случае нет выделенного "магического числа" 1, около которого точность максимальна (и плавно уменьшается при удалении от него в обе стороны). Вместо этого у нас есть "рабочий диапазон" (где шкала представимых чисел линейная) и "зона выбросов", где шаг между представимыми числами растет даже быстрее, чем в posit.

А мораль этой истории такова. Если уж даже для нашей задачки (которая, казалось бы, прямо напрашивается на решение в рамках posit-подхода) этот формат оказался непригодным, то тут есть большой повод задуматься: а так ли он универсален, как описано в этой статье? В общем, у меня есть такое подозрение, что posit-вычисления весьма хороши только для довольно узкого круга задач. А вот насчет их универсальности возникают большие сомнения. Имхо, по этому показателю плавающая точка IEEE соревнование с posit-форматом явно выигрывает.

UPD: Первоначально я по ошибке указал автором процитированных выше статей Испытания Posit по-взрослому и Испытания Posit по-взрослому. Спектральный анализ совсем другого человека. Мои искренние извинения обоим пострадавшим :-((

За ссылки на мои статьи спасибо, но почему их автором указан совсем другой человек?

За ссылки на мои статьи спасибо, но почему их автором указан совсем другой человек?

Черт возьми, это мой косяк! Тысяча извинений!! Позор на мою седую бороду! Сам не понимаю, как я умудрился перепутать. Слишком много вкладок было открыто, наверно.

Итого:

1) Автор рекомендованных выше статей:

Испытания Posit по-взрослому

Испытания Posit по-взрослому. Спектральный анализ

это, разумеется, @Refridgerator !

2) Сами статьи - отличные. ОЧЕНЬ рекомендую их посмотреть всем, кто интересуется темой.

3) Исправить свой глючный комментарий я уже не могу -(((

Попробую написать в поддержку Хабра...

Спасибо ещё раз) То, что IEEE 754 идеально подходит для инженеров - это не случайно, ведь именно для них он и был разработан, чтобы с запасом охватить все стандартные физические константы. Сам я тоже часто злоупотребляю NaN-ми, хотя и склонен считать, что это скорее bad practice - в частности, не все базы данных общего назначения его понимают.

свой формат компактного хранения real

Я ожидал по ссылке увидеть описание идеи, но не нашёл.

В целом, линейной и логарифмической шкалы не ограничиваются же. Можно ещё кучу разных придумать, адаптированных под конкретные задачи и рабочие диапазоны значений. Например, вот такая простая формула

\frac{x}{\left(x^n\pm 1\right)^{1/n}}

пакует значения от -бесконечности до +бесконечности к от -1 до 1 и обратно:

Предварительным линейным масштабированием можно задать и около-линейный диапазон, и точку отсчёта убывания точности, а затем квантовать под любое количество бит.

Я ожидал по ссылке увидеть описание идеи, но не нашёл.

Да, верно: за идеей надо лезть в код. Я не думал, что она будет кому-то интересна, так как мой случай слишком уж специфический: числа в диапазоне от 1 до 140000 пакуются в 10 бит. Естественно, с очень большим округлением, вытекающим из специфики задачи. А именно, от 1 до 128 шкала у меня линейная с шагом 1, а потом логарифмическая:

I4= 128+(Ln(R)-4.8484)*128
R= nint(exp(4.8484+(I-128)/128)

Также есть диапазон от 138770 до +бесконечности и NaN. Ну а потом у меня из трех 10-битных целых в другом месте собирается 4-байтное целое.

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

Да-да, именно так ;-)

Я согласен, что вместо заумной ссылки на код правильнее было бы его прямо сюда залить. Там всего-то ведь 30 строк, из которых треть - описание, а еще треть - заголовки. Собственно тело функции - это один пятистрочный IF. Но проблема в том, что мой код почему-то портится прямо в момент вставки из буфера обмена, то есть еще до того, как я смогу применить к нему тег "<>" и т.д.. Ладно бы там только пробелы терялись, но, например, символ умножения (который "*") тоже исчезает куда-то, и т.д.

Не сверять же мне потом вставленный код посимвольно

Поэтому лучше и не вставлять. А то вдруг кто-то его попробует скомпилировать... а у меня там при вставке хрен знает что покосилось...

Причем, что интересно, при вставке обычного текста у меня и пробелы, и звездочки вставляются правильно! И чужой код (на C, например) у меня тоже нормально вставляется. А вот именно мой фортран - ни фига. То ли у меня фаза Луны неправильная, то ли шутки в комментариях неприличные...

Кстати, если все комментарии вычистить, то код

все-таки вставился
C Вроде, тут покоцаны только пробелы:
  INTEGER*4 FUNCTION PACK_R_TO_INT(R)
  USE HEADERS, dummy_pack_r_to_int => pack_r_to_int
  real*4, intent(in) :: r
  if (isNan(r).or.(r <= 0)) then;   PACK_R_TO_INT=0
  else if (r <= 128) then;           PACK_R_TO_INT=nint(r)
  else if (r <= 138770) then;        PACK_R_TO_INT=128+nint((Log(R)-4.8484)*128.)      
  else;                             PACK_R_TO_INT=1024
  end if
  end

      REAL*4 FUNCTION UNPACK_R_FROM_INT(I)
      USE ABD_INC;  USE HEADERS, dummy_unpack_r_from_int => unpack_r_from_int
      integer*4, intent(in) ::  i
      if (i <= 0) then;             UNPACK_R_FROM_INT=R4_Nan()
      else if (i <= 128) then;       UNPACK_R_FROM_INT=i
      else if (i <= 1023) then;      UNPACK_R_FROM_INT=nint( exp(4.8484+real(I-128)/128.) )
      else if (i == 1024) then;      UNPACK_R_FROM_INT=138772
      else;                         UNPACK_R_FROM_INT=R4_Nan()
      end if
      end

Буду думать, что же у меня там в комментариях такое неправильное...

UPD. Мата вроде бы нет...

CCCCCCCC INTEGER4 FUNCTION PACK_R_TO_INT(R) CCCCCCCCCCCCCCCCC
CCCCCCCC INTEGER4 FUNCTION UNPACK_R_FROM_INT(I) CCCCCCCCCCCCCCCCC
C Функции пакуют real-число в диапазоне от 1 до 140000 в целую переменную C
C в диапазоне 0..1024 и обратно. Небольшие числа (от 1 до 144) кодируются точно C
C (погрешность менее 0.5), большие - приближенно (с округлением). C
C Формула кодирования для чисел от 129: C
C I*4= 128+(Ln(R)-4.8484)*128. C
C Для декодирования: R=nint(exp(4.8484+(I-128)/128) C
c...................................................................................c
C Отрицательные числа и 0 после кодирования-декодирования станут Nan, C
C а числа, превосходящие 140000 станут равны 140000. C
c...................................................................................C

...но все строки развалились на две половинки. Вместо квадратной псевдографической рамочки из символов "C" получилось что-то странное. Однако, звездочки и пробелы теперь почти все на месте! Прям какие-то чудеса...

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории