habrahabr

Реверс-инжиниринг π: как Pentium считал синусы быстрее всех

  • пятница, 14 ноября 2025 г. в 00:00:11
https://habr.com/ru/companies/otus/articles/965346/
TL;DR
  • В Pentium (P5) FPU синусы/косинусы/экспоненты/логарифмы считаются не CORDIC, а полиномиальными аппроксимациями с редукцией диапазона; коэффициенты оптимизированы (вероятно, по Ремезу, минимакс).

  • Константы лежат в отдельном constant ROM: 304 записи; секция экспоненты — 18 бит (17 бит экспоненты со смещением 0x0ffff + знаковый бит мантиссы), секция мантиссы — 68 бит (флаг + 67 бит значащей части).

  • Аппаратно ROM — две решётки (экспоненты/мантиссы) с перемежением бит для 8 констант в строке; выборка через мультиплексоры, драйверы строк — BiCMOS для работы на большой ёмкостной нагрузке.

  • Тригонометрия: таблично-полиномиальная схема с редукцией диапазона (для arctan — до ~[−1/64, 1/64]); tan получают делением sin на cos.

  • Экспонента: считают eˣ и переводят к 2ˣ через ln2; редукция использует таблицу 2^(n/128)−1. Логарифм: полином для atanh + таблица log₂(1+n/64) для нечётных n (каждая константа хранится «верхней» и «нижней» частью, суммарно ~107 бит для точности).

  • Автор извлёк содержимое ROM прямым реверсом (травление/шлифовка, микрофото, побитовая реконструкция) и сопоставил константы с алгоритмами.

В 1993 году компания Intel выпустила мощный процессор Pentium, положив начало долгоживущей серии высокопроизводительных чипов.1 примеч. Pentium содержит блок вычислений с плавающей точкой (FPU), который способен быстро вычислять такие функции, как синус, косинус, логарифм и экспоненциальную функцию. Но как именно Pentium выполняет эти вычисления?

Ранние процессоры Intel использовали алгоритмы класса CORDIC (итерационные алгоритмы на сдвиге и сложении), однако в Pentium компания перешла к полиномиальной аппроксимации этих трансцендентных функций, что позволило выполнять вычисления значительно быстрее. Коэффициенты полиномов были тщательно оптимизированы и сохранены в специальной постоянной памяти (ROM), встроенной в FPU. Несмотря на то, что Pentium — сложный микрочип, содержащий 3,1 миллиона транзисторов, рассмотреть эти транзисторы под микроскопом и прочитать хранящиеся там константы вполне возможно.

Первая часть статьи описывает, как реализована постоянная память с константами FPU на уровне аппаратуры. Вторая часть объясняет, как Pentium использует эти константы для вычисления функций sin, log и других.

На фотографии ниже показан кремниевый кристалл процессора Pentium, размером примерно с ноготь, под микроскопом. Основные функциональные блоки подписаны: блок вычислений с плавающей точкой находится в нижнем правом углу. Постоянная память с константами (выделена) расположена в нижней части этого блока. Выше находится ROM с микрокодом — микропрограммными инструкциями, задающими пошаговое выполнение сложных операций. При выполнении, например, инструкции вычисления синуса микрокод направляет FPU через десятки шагов, используя константы из ROM для вычисления аппроксимационного полинома.

Die photo of the Intel Pentium processor with the floating point constant ROM highlighted in red. Click this image (or any other) for a larger version.
Фотография кристалла процессора Intel Pentium, где постоянная память выделена красным. Кликните тут, чтобы открыть в полном масштабе.

Нахождение числа π в постоянной памяти

В двоичной системе π записывается как 11.00100100001111110… Что это значит? Число 11 слева от двоичной точки — это просто 3 в двоичном представлении. (Двоичная точка — то же самое, что десятичная, только для системы счисления по основанию 2.) Цифры справа от точки имеют значения 1/2, 1/4, 1/8 и так далее. Таким образом, двоичное значение 11.001001000011… соответствует 3 + 1/8 + 1/64 + 1/4096 + 1/8192 + …, что совпадает с десятичным значением числа π.

Так как π — иррациональное число, его битовая последовательность бесконечна и не повторяется; значение в ROM обрезано до 67 бит мантиссы и сохранено в виде числа с плавающей точкой.

Число с плавающей точкой представляется двумя частями: экспонентой и значащей частью (мантиссой). Числа с плавающей точкой включают как очень большие величины вроде 6.02×1023, так и очень малые вроде 1.055×10−34. В десятичной записи у 6.02×1023 значащая часть (мантисса) равна 6.02, а экспонента — 23 (1023). В двоичной системе число с плавающей точкой представляется аналогично — со значащей частью и экспонентой, — только значащая часть умножается на степень 2, а не 10. Например, π в формате с плавающей точкой записывается как 1.1001001…×21.

На диаграмме ниже показано, как π закодировано в чипе Pentium. При увеличении становится видна постоянная память с константами (constant ROM). Ещё большее увеличение небольшого участка ROM показывает строки транзисторов, в которых хранятся константы. Стрелки указывают на транзисторы, представляющие битовую последовательность 11001001, где бит 0 кодируется наличием транзистора (вертикальная белая линия), а бит 1 — его отсутствием (сплошной тёмный кремний). Каждый увеличенный чёрный прямоугольник внизу содержит два потенциальных транзистора и, соответственно, хранит два бита. Ключевая идея в том, что по рисунку «полосок» можно восстановить расположение транзисторов и, следовательно, значение каждой константы — в данном случае π.

A portion of the floating-point ROM, showing the value of pi. Click this image (or any other) for a larger version.
Фрагмент ROM блока вычислений с плавающей точкой, показывающий значение π. Кликните тут, чтобы открыть в полном масштабе.

Биты «разнесены» из-за того, что каждая ROM строка содержит восемь перемежающихся констант — это упрощает компоновку. Над битами ROM расположена схема мультиплексоров, которая выбирает нужную константу из восьми в активной строке. Иными словами, выбор строки и затем одной из восьми констант в этой строке даёт доступ к одной из 304 констант в ROM. В ROM хранится гораздо больше разрядов числа π, чем показано здесь; на диаграмме показаны 8 из 67 бит значащей части.

Реализация постоянной памяти с константами

ROM построена на транзисторах МОП — именно такие транзисторы используются во всех современных компьютерах. На схеме ниже показана структура МОП-транзистора. Интегральная схема создаётся на кремниевой подложке. Области кремния легируются примесями, формируя «диффузионные» области с требуемыми электрическими свойствами. Транзистор можно рассматривать как переключатель, который позволяет току течь между двумя диффузионными областями — истоком и стоком. Управление транзистором осуществляется затвором, выполненным из особого вида кремния — поликремния. Подача напряжения на затвор открывает канал между истоком и стоком; без напряжения ток перекрыт. В большинстве компьютеров применяются два типа МОП-транзисторов: n-канальные (NMOS) и p-канальные (PMOS). Оба типа имеют схожую конструкцию, но противоположное легирование: NMOS использует n-тип диффузионных областей, как показано ниже, тогда как PMOS — p-тип. Поскольку эти типы являются комплементарными, схемы, построенные на их сочетании, называются КМОП (CMOS).

Structure of a MOSFET in an integrated circuit.
Структура МОП-транзистора (MOSFET) в интегральной схеме.

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

One transistor in the constant ROM.
Один транзистор в постоянной памяти с константами.

На схеме ниже показано восемь бит хранения. Каждый из четырёх розовых кремниевых прямоугольников содержит два потенциальных транзистора. Если поликремниевый затвор пересекает кремний, формируется транзистор; если нет — транзистора нет. Когда активируется линия выбора (горизонтальный поликремний), она включает все транзисторы в этой строке. Если транзистор присутствует, соответствующий бит ROM равен 0, потому что транзистор притягивает выходную линию к «земле». Если транзистора нет, бит ROM равен 1. Таким образом, именно рисунок расположения транзисторов определяет данные, хранящиеся в ROM. Объём ROM — 26144 бита (304 слова по 86 бит), поэтому возможных транзисторов тоже 26144.

Eight bits of storage in the ROM.
Восемь бит хранения в ROM.

На фотографии ниже показан нижний слой металлизации (M1): вертикальные металлические проводники, формирующие выходы ROM и подводящие «землю» к ROM. (На схеме выше эти проводники изображены серыми линиями.) Поликремниевые транзисторы (или зазоры на их месте) едва заметны между металлическими линиями. Большинство маленьких кружков — это вольфрамовые контакты к кремнию или поликремнию; сравните с предыдущим фото. Другие кружки — это вольфрамовые переходы (виа) на верхний слой металлизации (M2), где проложены горизонтальные соединения, удалённые мной для этого снимка. Небольшие металлические «язычки» работают как перемычки между горизонтальными линиями выбора в M2 и поликремниевыми линиями выбора. Верхний слой металлизации (M3, не виден) несёт более толстые вертикальные шины основных магистралей питания и «земли» кристалла. Таким образом, три слоя металлизации чередуются по направлению — горизонтальная и вертикальная разводка — с переходами между слоями.

A closeup of the ROM showing the bottom metal layer.
Крупный план ROM с нижним слоем металлизации.

ROM реализована в виде двух решёток ячеек (ниже): одна хранит экспоненты, другая — значащие части (мантиссы), как показано ниже. Решётка экспонент (слева) содержит 38 строк и 144 столбца транзисторов, а решётка значащих частей (справа) — 38 строк и 544 столбца. Для более удобной компоновки каждая строка хранит восемь разных констант; биты перемежаются: сначала записан первый бит восьми констант, затем второй бит восьми констант и так далее. Таким образом, при 38 строках ROM содержит 304 константы; у каждой константы 18 бит в части экспоненты и 68 бит в части мантиссы.

A diagram of the constant ROM and supporting circuitry. Most of the significand ROM has been cut out to make it fit.
Схема постоянной памяти с константами и вспомогательными цепями. Большая часть области ROM со значащими частями вырезана, чтобы поместиться на рисунке.

Часть экспоненты у каждой константы состоит из 18 бит: 17 бит экспоненты и одного бита знака мантиссы, а значит и самой константы. Отдельного знакового бита для экспоненты нет, потому что экспонента хранится со смещением на 65535 (0x0ffff), что исключает отрицательные значения. Запись значащей части длиной 68 бит в ROM включает «загадочный» флаговый бит2 примеч., за которым следует 67-битная значащая часть; первый бит значащей — целая часть, остальные — дробная.3 примеч. Полное содержимое ROM приведено в приложении в конце статьи.

Чтобы выбрать конкретную константу, «схема выбора строки» между двумя секциями активирует одну из 38 строк. Эта строка выдаёт 144+544 бита на схему выборки над ROM. В этой схеме 86 мультиплексоров; каждый мультиплексор выбирает один бит из группы из восьми, тем самым выбирая нужную константу. Биты значащей части поступают в тракт данных блока вычислений с плавающей точкой, расположенный выше ROM. А вот схема обработки экспонент находится в левом верхнем углу FPU, на заметном расстоянии от ROM, поэтому биты экспонент передаются по шине в соответствующую схему.

Схема выбора строки состоит из логики для декодирования номера строки и мощных драйверов тока, которые активируют выбранную строку ROM. На фотографии ниже — крупный план двух драйверов строк рядом с ячейками ROM. Слева транзисторы PMOS и NMOS образуют вентиль выбора строки. Далее, более крупные NMOS и PMOS входят в состав драйвера. Крупные квадратные структуры — это биполярные транзисторы NPN; Pentium необычен тем, что сочетает биполярные транзисторы и КМОП, то есть использует технику BiCMOS.4 примеч. Каждый драйвер занимает по высоте четыре строки ROM, поэтому драйверы расположены горизонтально по четыре; на фото виден только один.

ROM drivers implemented with BiCMOS.
Драйверы ROM, реализованные на BiCMOS.

Структура блока вычислений с плавающей точкой

Блок вычислений с плавающей точкой организован так, что данные текут вертикально через горизонтально расположенные функциональные узлы, как показано ниже. Функциональные узлы — сумматоры, схемы сдвига, регистры и компараторы — выстроены рядами. Эта совокупность функциональных узлов, через которые проходит поток данных, называется трактом данных (datapath).5 примеч.

The datapath of the floating-point unit. The ROM is at the bottom.
Тракт данных блока вычислений с плавающей точкой. ROM находится внизу.

Каждый функциональный узел собран из ячеек — по одной на каждый бит, где слева расположен старший бит, а справа — младший. Все ячейки имеют одинаковую ширину — 38,5 мкм, — поэтому функциональные узлы можно соединять словно кирпичики Lego, минимизируя объёмы разводки. Высота функционального узла варьируется по необходимости, в зависимости от сложности схемы. Типичная разрядность узлов — 69 бит, но некоторые шире, из-за чего по краям тракта данных получается неровный контур.

Именно такая ячеечная конструкция объясняет, почему в каждой строке ROM хранится по восемь констант. Один бит в ROM требует всего одного транзистора, который намного уже, чем, скажем, сумматор. Поэтому размещение одного бита в каждой ячейке шириной 38,5 мкм растратило бы пространство. Сжатие бит ROM в узкую полосу тоже неэффективно: для подключения каждого бита ROM к соответствующему биту тракта данных потребовалась бы диагональная проводка. Если же в каждую ячейку поместить по восемь бит для восьми разных констант, ширина ячейки ROM совпадает с остальной частью тракта данных, и выравнивание бит сохраняется. В итоге компоновка ROM на кристалле получается плотной, эффективной и соответствующей ширине всего FPU.

Полиномиальная аппроксимация: не используйте ряд Тейлора

Далее перейдём от «железа» к константам. Если заглянуть в таблицу констант ROM в приложении, можно заметить, что многие значения близки к обратным величинам или обратным факториалам, но всё же не совпадают. Например, одна константа равна 0.1111111089 — это близко к 1/9, но заметно отличается. Другая почти равна 1/13! (факториал), но с погрешностью около 0,1 %. В чём дело?

Pentium использует полиномы для аппроксимации трансцендентных функций (синус, косинус, тангенс, арктангенс, а также показательную функцию 2x (по основанию 2) и логарифмы). Ранние блоки FPU Intel — от 8087 до 486 — применяли алгоритм CORDIC, который формировал результат по одному биту за раз. Однако в Pentium, благодаря быстрому умножителю и более объёмной ROM используются полиномы — вычисления получаются в два–три раза быстрее, чем в 486 (алгоритм CORDIC).

Из курса математического анализа вы можете помнить, что полином ряда Тейлора аппроксимирует функцию в окрестности некоторой точки (обычно 0). Например, уравнение ниже задаёт ряд Тейлора для синуса.

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

Plot of the sine function and the Taylor series approximation.
График функции синуса и её аппроксимации рядом Тейлора.

Проблема в том, что ряд Тейлора очень точен возле 0, но погрешность резко возрастает на границах диапазона аргумента, как видно на левом графике ниже. При реализации функции нам нужна точность по всему диапазону, а не только около 0, поэтому ряд Тейлора сам по себе не подходит.

The absolute error for a Taylor-series approximation to sine (5 terms), over two different argument ranges.
Абсолютная погрешность аппроксимации синуса рядом Тейлора (5 членов) на двух разных диапазонах аргумента.

Одно из улучшений называется «редукция диапазона» (range reduction): «сжимаем» аргумент к меньшему диапазону, попадая в «плоскую», точную часть.6 примеч. Правый график показывает погрешность (абсолютную ошибку) для ряда Тейлора на более узком интервале [−1/32, 1/32]. Погрешность падает драматически — примерно на 22 порядка (обратите внимание на шкалу). Однако на границах диапазона погрешность всё равно «взлетает» тем же образом. Как бы сильно мы ни сжимали диапазон, в середине ошибка почти отсутствует, а на краях остаётся значительной.7 примеч.

Как избавиться от ошибки на границах диапазона? Хитрость в том, чтобы «подкрутить» коэффициенты ряда Тейлора особым образом: немного увеличить ошибку в середине, но куда сильнее уменьшить её на краях. Поскольку мы хотим минимизировать максимальную ошибку на всём интервале (критерий минимакса), такой компромисс выгоден. Конкретно, коэффициенты можно оптимизировать с помощью процесса, называемого алгоритмом Ремеза.8 примеч. Как видно ниже, изменение коэффициентов менее чем на 1% радикально повышает точность. Оптимизированная функция (синяя) даёт значительно меньшую ошибку на всём диапазоне и потому гораздо лучше подходит для аппроксимации, чем ряд Тейлора (оранжевая).

Comparison of the absolute error from the Taylor series and a Remez-optimized polynomial, both with maximum term x9. This Remez polynomial is not one from the Pentium.
Сравнение абсолютной ошибки для ряда Тейлора и полинома, оптимизированного по Ремезу; в обоих случаях максимальная степень — x⁹. Этот полином Ремеза не взят из Pentium.

Подытожим: ряд Тейлора полезен в математическом анализе, но его не стоит использовать для приближённого вычисления функции. Намного лучшая аппроксимация получается при очень небольшом изменении коэффициентов по алгоритму Ремеза. Это объясняет, почему коэффициенты в ROM «почти, но не совсем» совпадают с рядом Тейлора.

Arctan

Теперь посмотрим на константы Pentium для разных трансцендентных функций. В постоянной памяти содержатся коэффициенты для двух полиномов arctan: один для одинарной точности, другой — для двойной. Эти полиномы почти совпадают с рядом Тейлора, но были изменены ради повышения точности. Кроме того, в ROM хранятся значения arctan(1/32) … arctan(32/32); процесс редукции диапазона использует эти константы вместе с тригонометрическим тождеством, чтобы сузить диапазон аргумента до [−1/64, 1/64].9 примеч.

На графике ниже показана ошибка полинома arctan из Pentium (синий) по сравнению с рядом Тейлора той же длины (оранжевый). Полином Pentium выигрывает благодаря оптимизации по Ремезу. Хотя полином Тейлора заметно более ровный в центре, его ошибка резко растёт у границ. Полином Pentium колеблется сильнее, но удерживает низкую ошибку на всём диапазоне. Вне этого диапазона ошибка у полинома Pentium взлетает, но это не имеет значения.

Comparison of the Pentium's double-precision arctan polynomial to the Taylor series.
Сравнение полинома arctan двойной точности из Pentium с рядом Тейлора.

Тригонометрические функции

У синуса и косинуса есть по две полиномиальные реализации: одна с 4 членами в ROM и одна с 6 членами в ROM. (Заметьте, коэффициенты, равные 1, в ROM не хранятся.) В таблице констант также есть 16 значений вроде sin(36/64) и cos(18/64), которые используются для редукции диапазона аргумента.¹⁰ примеч. Тангенс Pentium вычисляет делением синуса на косинус. График я не привожу, поскольку ошибка у реализации Pentium получилась хуже, чем у ряда Тейлора, так что либо у меня ошибка в коэффициенте, либо я делаю что-то не так.

Экспонента

В Pentium есть инструкция для вычисления функции 2ˣ.¹¹ примеч. В ROM лежат два набора полиномиальных коэффициентов для экспоненты: один с 6 членами и один с 11 членами. Любопытно, что полиномы в ROM вычисляют eˣ, а не 2ˣ. Следовательно, Pentium должен масштабировать аргумент через ln(2) — соответствующая константа есть в ROM. На графике ниже показано преимущество полинома Pentium по сравнению с полиномом ряда Тейлора.

The Pentium's 6-term exponential polynomial, compared with the Taylor series.
Экспоненциальный полином из 6 членов у Pentium в сравнении с рядом Тейлора.

Полином обслуживает узкий диапазон аргумента [−1/128, 1/128]. Обратите внимание: при вычислении функции 2ˣ в двоичной системе целая часть аргумента обрабатывается тривиально: она становится экспонентой результата. Поэтому функции нужно обрабатывать лишь диапазон [1, 2]. Для редукции диапазона в ROM хранится 64 значения вида 2n/128-1. Чтобы сузить диапазон с [1, 2] до [−1/128, 1/128], из аргумента вычитается ближайшее n/128, а затем результат умножается на соответствующую константу из ROM. Константы распределены неравномерно, по-видимому, ради точности: местами шаг 4/128, а местами 2/128.

Логарифм

Pentium умеет вычислять логарифмы по основанию 2.12 примеч. Коэффициенты задают полиномы для гиперболического арктангенса, который тесно связан с логарифмом. Подробности см. в примечаниях. В ROM также есть 64 константы для редукции диапазона: log₂(1 + n/64) для нечётных n от 1 до 63. Необычная особенность этих констант — разбиение каждой на две части для увеличения точности: верхняя часть имеет точность 40 бит, нижняя — 67 бит, в сумме давая 107-битную константу. Дополнительные биты нужны, потому что логарифмы сложно вычислять с высокой точностью.

Прочие константы

Набор инструкций x87 предоставляет прямой доступ к нескольким константам — 0, 1, π, log₂(10), log₂(e), log₁₀(2) и logₑ(2), — поэтому эти константы хранятся в ROM. (Эти логарифмы полезны для смены основания при вычислении логарифмов и экспоненциальных функций.) В ROM также есть другие константы для внутреннего использования блоком вычислений с плавающей точкой, такие как −1, 2, 7/8, 9/8, π/2, π/4 и 2·log₂(e). Кроме того, ROM содержит битовые маски для извлечения части машинного слова, например для доступа к 4-битовым BCD-цифрам внутри слова. Хотя большинство значений мне удалось интерпретировать, остаются и загадки — например, маска с загадочным значением 0x3e8287c. В конце ROM есть 34 неиспользованные записи; в них лежат слова, содержащие выразительное шестнадцатеричное значение 0xbad или, возможно, 0xbadfc — «плохая константа с плавающей точкой».

Как я исследовал ROM

Чтобы проанализировать Pentium, я удалил металлические и оксидные слои с помощью различных химикатов (серная кислота, фосфорная кислота, Whink). (Позже я обнаружил, что простая шлифовка кристалла тоже работает на удивление хорошо.) Затем я сделал множество микрофотографий ROM. Техпроцесс этого Pentium — 800 нм, то есть лишь чуть больше верхней границы видимого диапазона (≈700 нм). Поэтому кристалл можно изучать под оптическим микроскопом, хотя это уже близко к пределам. Чтобы восстановить содержимое ROM, я кропотливо просматривал снимки ROM, анализируя каждый из 26 144 бит и отмечая каждый транзистор. Разобравшись с форматом ROM, я написал программы, которые комбинируют простые функции во множестве сочетаний, чтобы вывести математические выражения вроде arctan(19/32) или log₂(10). Поскольку коэффициенты полиномов оптимизированы, а в считанных данных ROM есть битовые ошибки, программе потребовались проверки неточных совпадений — как численных, так и побитовых. Наконец, нужно было определить, как именно эти константы используются в алгоритмах.

Выводы

Изучая под микроскопом постоянную память с константами блока вычислений с плавающей точкой Pentium, можно извлечь все 304 хранящиеся там константы. Мне удалось определить смысл большинства из них и реконструировать некоторые алгоритмы вычислений с плавающей точкой, используемые в Pentium. Эти константы наглядно показывают, как полиномы могут эффективно вычислять трансцендентные функции. Хотя полиномы ряда Тейлора хорошо известны, их точность оказывается удивительно низкой, поэтому их следует избегать. Небольшие изменения коэффициентов по алгоритму Ремеза, напротив, дают значительно более качественные полиномы.

На уровне архитектуры это тот же класс решений: выбор инвариантов точности и латентности, разбиение на слои (редукция диапазона, таблицы, полином), контракт интерфейсов. На курсе Software Architect разбираются подобные инженерные компромиссы и шаблоны проектирования вычислительных подсистем.

Раньше я разбирал константы с плавающей точкой, хранящиеся в сопроцессоре 8087. В Pentium таких констант 304, тогда как в 8087 — всего 42, что позволяет поддерживать более эффективные алгоритмы. Кроме того, 8087 был внешним блоком вычислений с плавающей точкой, тогда как FPU Pentium встроен в сам процессор. Различия между 8087 (1980 год, 65 000 транзисторов) и Pentium (1993 год, 3,1 млн транзисторов) обусловлены экспоненциальным ростом числа транзисторов, описанным законом Мура.

Приложение: постоянная память с константами

В таблице ниже перечислены 304 константы из постоянной памяти (ROM) блока FPU Pentium. В первых четырёх столбцах показаны значения, хранящиеся в ROM: экспонента, бит знака, флаговый бит и значащая часть (мантисса). Чтобы избежать отрицательных экспонент, они записаны со смещением, равным 0x0ffff. Например, значение 0x0fffe соответствует экспоненте −1, а 0x10000 — экспоненте 1. Приблизительное десятичное значение константы приведено в столбце «value».

Специальные значения выделены цветом. В частности, «обычные» числа — чёрным. Константы с экспонентой, целиком состоящей из нулей, — синим; с экспонентой, целиком из единиц, — красным; с необычно большой или малой экспонентой — зелёным; по-видимому, это битовые маски, а не числа. Неиспользуемые записи — серым. Неточные константы (из-за оптимизации по Ремезу) помечены символом приближения «≈».

Эта информация получена в результате моего реверс-инжиниринга, поэтому возможны ошибки.

exp

S

F

significand

value

meaning

0

00000

0

0

07878787878787878

BCD mask by 4's

1

00000

0

0

007f807f807f807f8

BCD mask by 8's

2

00000

0

0

00007fff80007fff8

BCD mask by 16's

3

00000

0

0

000000007fffffff8

BCD mask by 32's

4

00000

0

0

78000000000000000

4-bit mask

5

00000

0

0

18000000000000000

2-bit mask

6

00000

0

0

27000000000000000

?

7

00000

0

0

363c0000000000000

?

8

00000

0

0

3e8287c0000000000

?

9

00000

0

0

470de4df820000000

213×1016

10

00000

0

0

5c3bd5191b525a249

2123/1017

11

00000

0

0

00000000000000007

3-bit mask

12

1ffff

1

1

7ffffffffffffffff

all 1's

13

00000

0

0

0000007ffffffffff

mask for 32-bit float

14

00000

0

0

00000000000003fff

mask for 64-bit float

15

00000

0

0

00000000000000000

all 0's

16

0ffff

0

0

40000000000000000

 1

1

17

10000

0

0

6a4d3c25e68dc57f2

 3.3219280949

log2(10)

18

0ffff

0

0

5c551d94ae0bf85de

 1.4426950409

log2(e)

19

10000

0

0

6487ed5110b4611a6

 3.1415926536

pi

20

0ffff

0

0

6487ed5110b4611a6

 1.5707963268

pi/2

21

0fffe

0

0

6487ed5110b4611a6

 0.7853981634

pi/4

22

0fffd

0

0

4d104d427de7fbcc5

 0.3010299957

log10(2)

23

0fffe

0

0

58b90bfbe8e7bcd5f

 0.6931471806

ln(2)

24

1ffff

0

0

40000000000000000

+infinity

25

0bfc0

0

0

40000000000000000

1/4 of smallest 80-bit denormal?

26

1ffff

1

0

60000000000000000

NaN (not a number)

27

0ffff

1

0

40000000000000000

-1

-1

28

10000

0

0

40000000000000000

 2

2

29

00000

0

0

00000000000000001

low bit

30

00000

0

0

00000000000000000

all 0's

31

00001

0

0

00000000000000000

single exponent bit

32

0fffe

0

0

58b90bfbe8e7bcd5e

 0.6931471806

ln(2)

33

0fffe

0

0

40000000000000000

 0.5

1/2! (exp Taylor series)

34

0fffc

0

0

5555555555555584f

 0.1666666667

≈1/3!

35

0fffa

0

0

555555555397fffd4

 0.0416666667

≈1/4!

36

0fff8

0

0

444444444250ced0c

 0.0083333333

≈1/5!

37

0fff5

0

0

5b05c3dd3901cea50

 0.0013888934

≈1/6!

38

0fff2

0

0

6806988938f4f2318

 0.0001984134

≈1/7!

39

0fffe

0

0

40000000000000000

 0.5

1/2! (exp Taylor series)

40

0fffc

0

0

5555555555555558e

 0.1666666667

≈1/3!

41

0fffa

0

0

5555555555555558b

 0.0416666667

≈1/4!

42

0fff8

0

0

444444444443db621

 0.0083333333

≈1/5!

43

0fff5

0

0

5b05b05b05afd42f4

 0.0013888889

≈1/6!

44

0fff2

0

0

68068068163b44194

 0.0001984127

≈1/7!

45

0ffef

0

0

6806806815d1b6d8a

 0.0000248016

≈1/8!

46

0ffec

0

0

5c778d8e0384c73ab

 2.755731e-06

≈1/9!

47

0ffe9

0

0

49f93e0ef41d6086b

 2.755731e-07

≈1/10!

48

0ffe5

0

0

6ba8b65b40f9c0ce8

 2.506632e-08

≈1/11!

49

0ffe2

0

0

47c5b695d0d1289a8

 2.088849e-09

≈1/12!

50

0fffd

0

0

6dfb23c651a2ef221

 0.4296133384

266/128-1

51

0fffd

0

0

75feb564267c8bf6f

 0.4609177942

270/128-1

52

0fffd

0

0

7e2f336cf4e62105d

 0.4929077283

274/128-1

53

0fffe

0

0

4346ccda249764072

 0.5255981507

278/128-1

54

0fffe

0

0

478d74c8abb9b15cc

 0.5590044002

282/128-1

55

0fffe

0

0

4bec14fef2727c5cf

 0.5931421513

286/128-1

56

0fffe

0

0

506333daef2b2594d

 0.6280274219

290/128-1

57

0fffe

0

0

54f35aabcfedfa1f6

 0.6636765803

294/128-1

58

0fffe

0

0

599d15c278afd7b60

 0.7001063537

298/128-1

59

0fffe

0

0

5e60f4825e0e9123e

 0.7373338353

2102/128-1

60

0fffe

0

0

633f8972be8a5a511

 0.7753764925

2106/128-1

61

0fffe

0

0

68396a503c4bdc688

 0.8142521755

2110/128-1

62

0fffe

0

0

6d4f301ed9942b846

 0.8539791251

2114/128-1

63

0fffe

0

0

7281773c59ffb139f

 0.8945759816

2118/128-1

64

0fffe

0

0

77d0df730ad13bb90

 0.9360617935

2122/128-1

65

0fffe

0

0

7d3e0c0cf486c1748

 0.9784560264

2126/128-1

66

0fffc

0

0

642e1f899b0626a74

 0.1956643920

233/128-1

67

0fffc

0

0

6ad8abf253fe1928c

 0.2086843236

235/128-1

68

0fffc

0

0

7195cda0bb0cb0b54

 0.2218460330

237/128-1

69

0fffc

0

0

7865b862751c90800

 0.2351510639

239/128-1

70

0fffc

0

0

7f48a09590037417f

 0.2486009772

241/128-1

71

0fffd

0

0

431f5d950a896dc70

 0.2621973504

243/128-1

72

0fffd

0

0

46a41ed1d00577251

 0.2759417784

245/128-1

73

0fffd

0

0

4a32af0d7d3de672e

 0.2898358734

247/128-1

74

0fffd

0

0

4dcb299fddd0d63b3

 0.3038812652

249/128-1

75

0fffd

0

0

516daa2cf6641c113

 0.3180796013

251/128-1

76

0fffd

0

0

551a4ca5d920ec52f

 0.3324325471

253/128-1

77

0fffd

0

0

58d12d497c7fd252c

 0.3469417862

255/128-1

78

0fffd

0

0

5c9268a5946b701c5

 0.3616090206

257/128-1

79

0fffd

0

0

605e1b976dc08b077

 0.3764359708

259/128-1

80

0fffd

0

0

6434634ccc31fc770

 0.3914243758

261/128-1

81

0fffd

0

0

68155d44ca973081c

 0.4065759938

263/128-1

82

0fffd

1

0

4cee3bed56eedb76c

-0.3005101637

2-66/128-1

83

0fffd

1

0

50c4875296f5bc8b2

-0.3154987885

2-70/128-1

84

0fffd

1

0

5485c64a56c12cc8a

-0.3301662380

2-74/128-1

85

0fffd

1

0

58326c4b169aca966

-0.3445193942

2-78/128-1

86

0fffd

1

0

5bcaea51f6197f61f

-0.3585649920

2-82/128-1

87

0fffd

1

0

5f4faef0468eb03de

-0.3723096215

2-86/128-1

88

0fffd

1

0

62c12658d30048af2

-0.3857597319

2-90/128-1

89

0fffd

1

0

661fba6cdf48059b2

-0.3989216343

2-94/128-1

90

0fffd

1

0

696bd2c8dfe7a5ffb

-0.4118015042

2-98/128-1

91

0fffd

1

0

6ca5d4d0ec1916d43

-0.4244053850

2-102/128-1

92

0fffd

1

0

6fce23bceb994e239

-0.4367391907

2-106/128-1

93

0fffd

1

0

72e520a481a4561a5

-0.4488087083

2-110/128-1

94

0fffd

1

0

75eb2a8ab6910265f

-0.4606196011

2-114/128-1

95

0fffd

1

0

78e09e696172efefc

-0.4721774108

2-118/128-1

96

0fffd

1

0

7bc5d73c5321bfb9e

-0.4834875605

2-122/128-1

97

0fffd

1

0

7e9b2e0c43fcf88c8

-0.4945553570

2-126/128-1

98

0fffc

1

0

53c94402c0c863f24

-0.1636449102

2-33/128-1

99

0fffc

1

0

58661eccf4ca790d2

-0.1726541162

2-35/128-1

100

0fffc

1

0

5cf6413b5d2cca73f

-0.1815662751

2-37/128-1

101

0fffc

1

0

6179ce61cdcdce7db

-0.1903824324

2-39/128-1

102

0fffc

1

0

65f0e8f35f84645cf

-0.1991036222

2-41/128-1

103

0fffc

1

0

6a5bb3437adf1164b

-0.2077308674

2-43/128-1

104

0fffc

1

0

6eba4f46e003a775a

-0.2162651800

2-45/128-1

105

0fffc

1

0

730cde94abb7410d5

-0.2247075612

2-47/128-1

106

0fffc

1

0

775382675996699ad

-0.2330590011

2-49/128-1

107

0fffc

1

0

7b8e5b9dc385331ad

-0.2413204794

2-51/128-1

108

0fffc

1

0

7fbd8abc1e5ee49f2

-0.2494929652

2-53/128-1

109

0fffd

1

0

41f097f679f66c1db

-0.2575774171

2-55/128-1

110

0fffd

1

0

43fcb5810d1604f37

-0.2655747833

2-57/128-1

111

0fffd

1

0

46032dbad3f462152

-0.2734860021

2-59/128-1

112

0fffd

1

0

48041035735be183c

-0.2813120013

2-61/128-1

113

0fffd

1

0

49ff6c57a12a08945

-0.2890536989

2-63/128-1

114

0fffd

1

0

555555555555535f0

-0.3333333333

≈-1/3 (arctan Taylor series)

115

0fffc

0

0

6666666664208b016

 0.2

≈ 1/5

116

0fffc

1

0

492491e0653ac37b8

-0.1428571307

≈-1/7

117

0fffb

0

0

71b83f4133889b2f0

 0.1110544094

≈ 1/9

118

0fffd

1

0

55555555555555543

-0.3333333333

≈-1/3 (arctan Taylor series)

119

0fffc

0

0

66666666666616b73

 0.2

≈ 1/5

120

0fffc

1

0

4924924920fca4493

-0.1428571429

≈-1/7

121

0fffb

0

0

71c71c4be6f662c91

 0.1111111089

≈ 1/9

122

0fffb

1

0

5d16e0bde0b12eee8

-0.0909075848

≈-1/11

123

0fffb

0

0

4e403be3e3c725aa0

 0.0764169081

≈ 1/13

124

00000

0

0

40000000000000000

single bit mask

125

0fff9

0

0

7ff556eea5d892a14

 0.0312398334

arctan(1/32)

126

0fffa

0

0

7fd56edcb3f7a71b6

 0.0624188100

arctan(2/32)

127

0fffb

0

0

5fb860980bc43a305

 0.0934767812

arctan(3/32)

128

0fffb

0

0

7f56ea6ab0bdb7196

 0.1243549945

arctan(4/32)

129

0fffc

0

0

4f5bbba31989b161a

 0.1549967419

arctan(5/32)

130

0fffc

0

0

5ee5ed2f396c089a4

 0.1853479500

arctan(6/32)

131

0fffc

0

0

6e435d4a498288118

 0.2153576997

arctan(7/32)

132

0fffc

0

0

7d6dd7e4b203758ab

 0.2449786631

arctan(8/32)

133

0fffd

0

0

462fd68c2fc5e0986

 0.2741674511

arctan(9/32)

134

0fffd

0

0

4d89dcdc1faf2f34e

 0.3028848684

arctan(10/32)

135

0fffd

0

0

54c2b6654735276d5

 0.3310960767

arctan(11/32)

136

0fffd

0

0

5bd86507937bc239c

 0.3587706703

arctan(12/32)

137

0fffd

0

0

62c934e5286c95b6d

 0.3858826694

arctan(13/32)

138

0fffd

0

0

6993bb0f308ff2db2

 0.4124104416

arctan(14/32)

139

0fffd

0

0

7036d3253b27be33e

 0.4383365599

arctan(15/32)

140

0fffd

0

0

76b19c1586ed3da2b

 0.4636476090

arctan(16/32)

141

0fffd

0

0

7d03742d50505f2e3

 0.4883339511

arctan(17/32)

142

0fffe

0

0

4195fa536cc33f152

 0.5123894603

arctan(18/32)

143

0fffe

0

0

4495766fef4aa3da8

 0.5358112380

arctan(19/32)

144

0fffe

0

0

47802eaf7bfacfcdb

 0.5585993153

arctan(20/32)

145

0fffe

0

0

4a563964c238c37b1

 0.5807563536

arctan(21/32)

146

0fffe

0

0

4d17c07338deed102

 0.6022873461

arctan(22/32)

147

0fffe

0

0

4fc4fee27a5bd0f68

 0.6231993299

arctan(23/32)

148

0fffe

0

0

525e3e8c9a7b84921

 0.6435011088

arctan(24/32)

149

0fffe

0

0

54e3d5ee24187ae45

 0.6632029927

arctan(25/32)

150

0fffe

0

0

5756261c5a6c60401

 0.6823165549

arctan(26/32)

151

0fffe

0

0

59b598e48f821b48b

 0.7008544079

arctan(27/32)

152

0fffe

0

0

5c029f15e118cf39e

 0.7188299996

arctan(28/32)

153

0fffe

0

0

5e3daef574c579407

 0.7362574290

arctan(29/32)

154

0fffe

0

0

606742dc562933204

 0.7531512810

arctan(30/32)

155

0fffe

0

0

627fd7fd5fc7deaa4

 0.7695264804

arctan(31/32)

156

0fffe

0

0

6487ed5110b4611a6

 0.7853981634

arctan(32/32)

157

0fffc

1

0

55555555555555555

-0.1666666667

≈-1/3! (sin Taylor series)

158

0fff8

0

0

44444444444443e35

 0.0083333333

≈ 1/5!

159

0fff2

1

0

6806806806773c774

-0.0001984127

≈-1/7!

160

0ffec

0

0

5c778e94f50956d70

 2.755732e-06

≈ 1/9!

161

0ffe5

1

0

6b991122efa0532f0

-2.505209e-08

≈-1/11!

162

0ffde

0

0

58303f02614d5e4d8

 1.604139e-10

≈ 1/13!

163

0fffd

1

0

7fffffffffffffffe

-0.5

≈-1/2! (cos Taylor series)

164

0fffa

0

0

55555555555554277

 0.0416666667

≈ 1/4!

165

0fff5

1

0

5b05b05b05a18a1ba

-0.0013888889

≈-1/6!

166

0ffef

0

0

680680675b559f2cf

 0.0000248016

≈ 1/8!

167

0ffe9

1

0

49f93af61f5349300

-2.755730e-07

≈-1/10!

168

0ffe2

0

0

47a4f2483514c1af8

 2.085124e-09

≈ 1/12!

169

0fffc

1

0

55555555555555445

-0.1666666667

≈-1/3! (sin Taylor series)

170

0fff8

0

0

44444444443a3fdb6

 0.0083333333

≈ 1/5!

171

0fff2

1

0

68068060b2044e9ae

-0.0001984127

≈-1/7!

172

0ffec

0

0

5d75716e60f321240

 2.785288e-06

≈ 1/9!

173

0fffd

1

0

7fffffffffffffa28

-0.5

≈-1/2! (cos Taylor series)

174

0fffa

0

0

555555555539cfae6

 0.0416666667

≈ 1/4!

175

0fff5

1

0

5b05b050f31b2e713

-0.0013888889

≈-1/6!

176

0ffef

0

0

6803988d56e3bff10

 0.0000247989

≈ 1/8!

177

0fffe

0

0

44434312da70edd92

 0.5333026735

sin(36/64)

178

0fffe

0

0

513ace073ce1aac13

 0.6346070800

sin(44/64)

179

0fffe

0

0

5cedda037a95df6ee

 0.7260086553

sin(52/64)

180

0fffe

0

0

672daa6ef3992b586

 0.8060811083

sin(60/64)

181

0fffd

0

0

470df5931ae1d9460

 0.2775567516

sin(18/64)

182

0fffd

0

0

5646f27e8bd65cbe4

 0.3370200690

sin(22/64)

183

0fffd

0

0

6529afa7d51b12963

 0.3951673302

sin(26/64)

184

0fffd

0

0

73a74b8f52947b682

 0.4517714715

sin(30/64)

185

0fffe

0

0

6c4741058a93188ef

 0.8459244992

cos(36/64)

186

0fffe

0

0

62ec41e9772401864

 0.7728350058

cos(44/64)

187

0fffe

0

0

5806149bd58f7d46d

 0.6876855622

cos(52/64)

188

0fffe

0

0

4bc044c9908390c72

 0.5918050751

cos(60/64)

189

0fffe

0

0

7af8853ddbbe9ffd0

 0.9607092430

cos(18/64)

190

0fffe

0

0

7882fd26b35b03d34

 0.9414974631

cos(22/64)

191

0fffe

0

0

7594fc1cf900fe89e

 0.9186091558

cos(26/64)

192

0fffe

0

0

72316fe3386a10d5a

 0.8921336994

cos(30/64)

193

0ffff

0

0

48000000000000000

 1.125

9/8

194

0fffe

0

0

70000000000000000

 0.875

7/8

195

0ffff

0

0

5c551d94ae0bf85de

 1.4426950409

log2(e)

196

10000

0

0

5c551d94ae0bf85de

 2.8853900818

2log2(e)

197

0fffb

0

0

7b1c2770e81287c11

 0.1202245867

≈1/(41⋅3⋅ln(2)) (atanh series for log)

198

0fff9

0

0

49ddb14064a5d30bd

 0.0180336880

≈1/(42⋅5⋅ln(2))

199

0fff6

0

0

698879b87934f12e0

 0.0032206148

≈1/(43⋅7⋅ln(2))

200

0fffa

0

0

51ff4ffeb20ed1749

 0.0400377512

≈(ln(2)/2)2/3 (atanh series for log)

201

0fff6

0

0

5e8cd07eb1827434a

 0.0028854387

≈(ln(2)/2)4/5

202

0fff3

0

0

40e54061b26dd6dc2

 0.0002475567

≈(ln(2)/2)6/7

203

0ffef

0

0

61008a69627c92fb9

 0.0000231271

≈(ln(2)/2)8/9

204

0ffec

0

0

4c41e6ced287a2468

 2.272648e-06

≈(ln(2)/2)10/11

205

0ffe8

0

0

7dadd4ea3c3fee620

 2.340954e-07

≈(ln(2)/2)12/13

206

0fff9

0

0

5b9e5a170b8000000

 0.0223678130

log2(1+1/64) top bits

207

0fffb

0

0

43ace37e8a8000000

 0.0660892054

log2(1+3/64) top bits

208

0fffb

0

0

6f210902b68000000

 0.1085244568

log2(1+5/64) top bits

209

0fffc

0

0

4caba789e28000000

 0.1497471195

log2(1+7/64) top bits

210

0fffc

0

0

6130af40bc0000000

 0.1898245589

log2(1+9/64) top bits

211

0fffc

0

0

7527b930c98000000

 0.2288186905

log2(1+11/64) top bits

212

0fffd

0

0

444c1f6b4c0000000

 0.2667865407

log2(1+13/64) top bits

213

0fffd

0

0

4dc4933a930000000

 0.3037807482

log2(1+15/64) top bits

214

0fffd

0

0

570068e7ef8000000

 0.3398500029

log2(1+17/64) top bits

215

0fffd

0

0

6002958c588000000

 0.3750394313

log2(1+19/64) top bits

216

0fffd

0

0

68cdd829fd8000000

 0.4093909361

log2(1+21/64) top bits

217

0fffd

0

0

7164beb4a58000000

 0.4429434958

log2(1+23/64) top bits

218

0fffd

0

0

79c9aa879d8000000

 0.4757334310

log2(1+25/64) top bits

219

0fffe

0

0

40ff6a2e5e8000000

 0.5077946402

log2(1+27/64) top bits

220

0fffe

0

0

450327ea878000000

 0.5391588111

log2(1+29/64) top bits

221

0fffe

0

0

48f107509c8000000

 0.5698556083

log2(1+31/64) top bits

222

0fffe

0

0

4cc9f1aad28000000

 0.5999128422

log2(1+33/64) top bits

223

0fffe

0

0

508ec1fa618000000

 0.6293566201

log2(1+35/64) top bits

224

0fffe

0

0

5440461c228000000

 0.6582114828

log2(1+37/64) top bits

225

0fffe

0

0

57df3fd0780000000

 0.6865005272

log2(1+39/64) top bits

226

0fffe

0

0

5b6c65a9d88000000

 0.7142455177

log2(1+41/64) top bits

227

0fffe

0

0

5ee863e4d40000000

 0.7414669864

log2(1+43/64) top bits

228

0fffe

0

0

6253dd2c1b8000000

 0.7681843248

log2(1+45/64) top bits

229

0fffe

0

0

65af6b4ab30000000

 0.7944158664

log2(1+47/64) top bits

230

0fffe

0

0

68fb9fce388000000

 0.8201789624

log2(1+49/64) top bits

231

0fffe

0

0

6c39049af30000000

 0.8454900509

log2(1+51/64) top bits

232

0fffe

0

0

6f681c731a0000000

 0.8703647196

log2(1+53/64) top bits

233

0fffe

0

0

72896372a50000000

 0.8948177633

log2(1+55/64) top bits

234

0fffe

0

0

759d4f80cb8000000

 0.9188632373

log2(1+57/64) top bits

235

0fffe

0

0

78a450b8380000000

 0.9425145053

log2(1+59/64) top bits

236

0fffe

0

0

7b9ed1c6ce8000000

 0.9657842847

log2(1+61/64) top bits

237

0fffe

0

0

7e8d3845df0000000

 0.9886846868

log2(1+63/64) top bits

238

0ffd0

1

0

6eb3ac8ec0ef73f7b

-1.229037e-14

log2(1+1/64) bottom bits

239

0ffcd

1

0

654c308b454666de9

-1.405787e-15

log2(1+3/64) bottom bits

240

0ffd2

0

0

5dd31d962d3728cbd

 4.166652e-14

log2(1+5/64) bottom bits

241

0ffd3

0

0

70d0fa8f9603ad3a6

 1.002010e-13

log2(1+7/64) bottom bits

242

0ffd1

0

0

765fba4491dcec753

 2.628429e-14

log2(1+9/64) bottom bits

243

0ffd2

1

0

690370b4a9afdc5fb

-4.663533e-14

log2(1+11/64) bottom bits

244

0ffd4

0

0

5bae584b82d3cad27

 1.628582e-13

log2(1+13/64) bottom bits

245

0ffd4

0

0

6f66cc899b64303f7

 1.978889e-13

log2(1+15/64) bottom bits

246

0ffd4

1

0

4bc302ffa76fafcba

-1.345799e-13

log2(1+17/64) bottom bits

247

0ffd2

1

0

7579aa293ec16410a

-5.216949e-14

log2(1+19/64) bottom bits

248

0ffcf

0

0

509d7c40d7979ec5b

 4.475041e-15

log2(1+21/64) bottom bits

249

0ffd3

1

0

4a981811ab5110ccf

-6.625289e-14

log2(1+23/64) bottom bits

250

0ffd4

1

0

596f9d730f685c776

-1.588702e-13

log2(1+25/64) bottom bits

251

0ffd4

1

0

680cc6bcb9bfa9853

-1.848298e-13

log2(1+27/64) bottom bits

252

0ffd4

0

0

5439e15a52a31604a

 1.496156e-13

log2(1+29/64) bottom bits

253

0ffd4

0

0

7c8080ecc61a98814

 2.211599e-13

log2(1+31/64) bottom bits

254

0ffd3

1

0

6b26f28dbf40b7bc0

-9.517022e-14

log2(1+33/64) bottom bits

255

0ffd5

0

0

554b383b0e8a55627

 3.030245e-13

log2(1+35/64) bottom bits

256

0ffd5

0

0

47c6ef4a49bc59135

 2.550034e-13

log2(1+37/64) bottom bits

257

0ffd5

0

0

4d75c658d602e66b0

 2.751934e-13

log2(1+39/64) bottom bits

258

0ffd4

1

0

6b626820f81ca95da

-1.907530e-13

log2(1+41/64) bottom bits

259

0ffd3

0

0

5c833d56efe4338fe

 8.216774e-14

log2(1+43/64) bottom bits

260

0ffd5

0

0

7c5a0375163ec8d56

 4.417857e-13

log2(1+45/64) bottom bits

261

0ffd5

1

0

5050809db75675c90

-2.853343e-13

log2(1+47/64) bottom bits

262

0ffd4

1

0

7e12f8672e55de96c

-2.239526e-13

log2(1+49/64) bottom bits

263

0ffd5

0

0

435ebd376a70d849b

 2.393466e-13

log2(1+51/64) bottom bits

264

0ffd2

1

0

6492ba487dfb264b3

-4.466345e-14

log2(1+53/64) bottom bits

265

0ffd5

1

0

674e5008e379faa7c

-3.670163e-13

log2(1+55/64) bottom bits

266

0ffd5

0

0

5077f1f5f0cc82aab

 2.858817e-13

log2(1+57/64) bottom bits

267

0ffd2

0

0

5007eeaa99f8ef14d

 3.554090e-14

log2(1+59/64) bottom bits

268

0ffd5

0

0

4a83eb6e0f93f7a64

 2.647316e-13

log2(1+61/64) bottom bits

269

0ffd3

0

0

466c525173dae9cf5

 6.254831e-14

log2(1+63/64) bottom bits

270

0badf

0

1

40badfc0badfc0bad

unused

271

0badf

0

1

40badfc0badfc0bad

unused

272

0badf

0

1

40badfc0badfc0bad

unused

273

0badf

0

1

40badfc0badfc0bad

unused

274

0badf

0

1

40badfc0badfc0bad

unused

275

0badf

0

1

40badfc0badfc0bad

unused

276

0badf

0

1

40badfc0badfc0bad

unused

277

0badf

0

1

40badfc0badfc0bad

unused

278

0badf

0

1

40badfc0badfc0bad

unused

279

0badf

0

1

40badfc0badfc0bad

unused

280

0badf

0

1

40badfc0badfc0bad

unused

281

0badf

0

1

40badfc0badfc0bad

unused

282

0badf

0

1

40badfc0badfc0bad

unused

283

0badf

0

1

40badfc0badfc0bad

unused

284

0badf

0

1

40badfc0badfc0bad

unused

285

0badf

0

1

40badfc0badfc0bad

unused

286

0badf

0

1

40badfc0badfc0bad

unused

287

0badf

0

1

40badfc0badfc0bad

unused

288

0badf

0

1

40badfc0badfc0bad

unused

289

0badf

0

1

40badfc0badfc0bad

unused

290

0badf

0

1

40badfc0badfc0bad

unused

291

0badf

0

1

40badfc0badfc0bad

unused

292

0badf

0

1

40badfc0badfc0bad

unused

293

0badf

0

1

40badfc0badfc0bad

unused

294

0badf

0

1

40badfc0badfc0bad

unused

295

0badf

0

1

40badfc0badfc0bad

unused

296

0badf

0

1

40badfc0badfc0bad

unused

297

0badf

0

1

40badfc0badfc0bad

unused

298

0badf

0

1

40badfc0badfc0bad

unused

299

0badf

0

1

40badfc0badfc0bad

unused

300

0badf

0

1

40badfc0badfc0bad

unused

301

0badf

0

1

40badfc0badfc0bad

unused

302

0badf

0

1

40badfc0badfc0bad

unused

303

0badf

0

1

40badfc0badfc0bad

unused

Примечания (осторожно, много текста)
  1. В этой статье я разбираю версию «P5» оригинального процессора Pentium. Легко запутаться во всех «Pentium», потому что «Pentium» стал брендом с множеством микроархитектур, линеек и продуктов. Оригинальный Pentium (1993) сменили Pentium Pro (1995), Pentium II (1997) и так далее.

Оригинальный Pentium использовал микроархитектуру P5 — суперскалярную микроархитектуру, которая была передовой, но всё ещё исполняла инструкции в порядке поступления, как традиционные микропроцессоры. Оригинальный Pentium пережил несколько существенных ревизий. Первым продуктом был 80501 (кодовое имя P5) с 3,1 млн транзисторов. Энергопотребление этих чипов оказалось разочаровывающим, поэтому Intel улучшила кристалл и выпустила 80502 с кодовым именем P54C. По топологии на кристалле P5 и P54C выглядят почти одинаково, но в P54C добавили схемы для многопроцессорности, что увеличило число транзисторов до 3,3 млн. Крупнейшим изменением в оригинальном Pentium стал Pentium MMX с номером 80503 и кодовым именем P55C. Pentium MMX добавил 57 векторных инструкций и содержал 4,5 млн транзисторов. Блок с плавающей запятой (FPU) был переработан в MMX, но константы, вероятно, остались теми же. 

2. Я не знаю, что означает флаговый бит в ПЗУ; я условно называю его «флагом». Моя смелая догадка — он помечает те записи ПЗУ, которые нужно исключить из контрольной суммы при проверке ПЗУ. 

3. Внутренне мантисса имеет один целочисленный (старший) бит, а остальное — дробная часть, поэтому двоичная точка (аналог десятичной точки) стоит после первого бита. Однако это не единственный способ представления мантиссы. Формат x87 с расширенной двойной точностью (80 бит) использует тот же подход. А вот 32-битный (одинарной точности) и 64-битный (двойной точности) форматы опускают первый бит и используют «имплицитную единицу». Это даёт «бесплатно» ещё один бит мантиссы, поскольку в нормальных случаях первый бит мантиссы равен 1. 

4. Необычная особенность Pentium — использование биполярных транзисторов NPN вместе с КМОП-схемами; такую технологию называют BiCMOS. Добавив несколько дополнительных этапов к стандартному КМОП-процессу, можно сформировать биполярные транзисторы. В Pentium схемы BiCMOS применялись широко, поскольку они уменьшали задержки сигналов до 35%. Intel также использовала BiCMOS в процессорах Pentium Pro, Pentium II, Pentium III и Xeon (но не в Pentium MMX). Однако по мере снижения рабочих напряжений выгода от биполярных транзисторов уменьшалась, и от BiCMOS в итоге отказались.

В ПЗУ с константами схемы BiCMOS улучшают работу цепей выбора строк. Каждая линия выбора строки очень длинная и подключена к сотням транзисторов, поэтому ёмкостная нагрузка велика. Благодаря быстрому и «мощному» NPN-транзистору драйвер BiCMOS обеспечивает меньшую задержку при высокой нагрузке по сравнению с обычным КМОП-драйвером.

Типичный инвертор BiCMOS. По материалам «A 3.3V 0.6µm BiCMOS superscalar microprocessor».
Типичный инвертор BiCMOS. По материалам «A 3.3V 0.6µm BiCMOS superscalar microprocessor».

Такую BiCMOS-логику также называют BiNMOS или BinMOS, потому что в выходном каскаде используются биполярный транзистор и NMOS-транзистор. Подробнее о схемах BiCMOS в Pentium — в моей статье «Стандартные ячейки: взгляд на отдельные логические элементы процессора Pentium».

5. Целочисленный вычислительный блок Pentium построен аналогично: горизонтально «сложенные» функциональные узлы образуют тракт данных. Каждая ячейка в целочисленном блоке значительно шире, чем плавающая (64 мкм против 38,5 мкм). Однако целочисленный блок всего 32-разрядный, тогда как блок с плавающей запятой имеет ширину порядка 69 бит, поэтому по общей ширине FPU получается больше. 

6. Мне не нравится говорить о «диапазоне аргумента», поскольку «range» — это обычно область значений функции (выход), а вход — «domain». Но термин «range reduction» («приведение диапазона») — общепринятый, так что оставлю его. 

7. Есть причина, почему кривая ошибки выглядит похожей, даже если сузить диапазон. Погрешность от усечённого ряда Тейлора примерно равна следующему члену ряда; в нашем случае ошибка примерно равна −x11/11! или O(x11). Отсюда видно, почему приведение диапазона так эффективно: если уменьшить диапазон в 2 раза, ошибка уменьшится на огромный множитель 211. Но это также объясняет, почему форма кривой ошибки сохраняется: кривая всё ещё ~x11, только шкалы по осям меняются. 

8. Коэффициенты для Pentium, вероятно, получены с помощью алгоритма Ремеза; см. «Проверка вычислений с плавающей запятой». Преимущества полинома Ремеза по сравнению с рядом Тейлора обсуждаются в «Лучшие аппроксимации функций: Тейлор против Ремеза». Описание алгоритма Ремеза дано в книге «Элементарные функции: алгоритмы и реализация», где есть и другая релевантная информация о полиномиальных аппроксимациях и приведении диапазона. Подробнее о полиномиальных аппроксимациях см. «Численное вычисление экспоненты с помощью полиномиальных аппроксимаций» и «Восемь полезных полиномиальных аппроксимаций для sinf(3)».

Полином Ремеза на графике синуса — не тот полином, который использует Pentium; он сгенерирован для иллюстрации с помощью lolremez, полезного инструмента. Конкретный полином таков:

9.9997938808335731e-1 ⋅ x − 1.6662438518867169e-1 ⋅ x3 + 8.3089850302282266e-3 ⋅ x5 − 1.9264997445395096e-4 ⋅ x7 + 2.1478735041839789e-6 ⋅ x9

Ниже показана ошибка для этого полинома. Обратите внимание, что ошибка колеблется между верхней и нижней границами. Это типичный вид полинома Ремеза. Для сравнения: у ряда Тейлора ошибка почти нулевая посередине и резко возрастает на краях. Этот полином Ремеза оптимизирован для диапазона [-π, π]; вне этого диапазона ошибка «взрывается». Ключевой момент в том, что полином Ремеза распределяет ошибку внутри диапазона. Это минимизирует максимальную ошибку (минимакс).

Ошибка полинома Ремеза, оптимизированного для синуса.
Ошибка полинома Ремеза, оптимизированного для синуса.

9. Я полагаю, что аргумент arctan приводится к диапазону [-1/64, 1/64]. Это можно сделать с помощью тригонометрической тождественности arctan(x) = arctan((x−c)/(1+xc)) + arctan(c). Идея в том, чтобы выбрать c как значение вида n/32, ближайшее к x. В результате x−c окажется в требуемом диапазоне, и первый arctan можно вычислить полиномом. Другой член, arctan(c), берётся из таблицы значений в ПЗУ. Инструкция FPATAN («частичный арктангенс») принимает два аргумента, x и y, и возвращает atan(y/x); это упрощает работу с плоскими координатами. В этом случае тождество принимает вид arctan(y/x) = arctan((y−tx)/(x+ty)) + arctan c. Операция деления в некоторых случаях может приводить к срабатыванию ошибки FDIV; см. «Вычислительные аспекты истории с Pentium».

10. У Pentium есть несколько тригонометрических инструкций: FSIN, FCOS и FSINCOS возвращают синус, косинус или оба сразу (что почти так же быстро, как вычислить любую из них по отдельности). FPTAN возвращает «частичный тангенс», состоящий из двух чисел, которые нужно разделить, чтобы получить tan. (Это связано с ограничениями исходного сопроцессора 8087.) Pentium возвращает тангенс первым числом и константу 1 — вторым, сохраняя семантику FPTAN, но делая использование удобнее.

Приведение диапазона, вероятно, основано на тождестве sin(a+b) = sin(a)cos(b) + cos(a)sin(b). Чтобы вычислить sin(x), выбирают b как ближайшую константу из таблицы, n/64, затем получают a = x − b. Значение a будет приведено по диапазону, так что sin(a) можно вычислить по полиному. Величины sin(b) и cos(b) доступны из таблицы. Искомое значение sin(x) затем вычисляется умножениями и сложением по указанному тождеству. Косинус можно получить аналогично. Заметим, что cos(a+b) = cos(a)cos(b) − sin(a)sin(b); правые части совпадают с формулой для sin(a+b), только комбинируются иначе. Следовательно, вычислив правые части, можно собрать синус, косинус или сразу оба. Тангенс Pentium получает делением синуса на косинус. Это может вызвать ошибку деления FDIV; см. «Вычислительные аспекты истории с Pentium».

См. также «Тайминги инструкций Агнера Фога»; длительности различных операций дают подсказки о том, как они реализованы. Например, FPTAN выполняется дольше, чем FSINCOS, потому что тангенс получается делением синуса на косинус. 

11. Для экспонент существует инструкция F2XM1, вычисляющая 2x − 1; вычитание 1 повышает точность. Конкретно, 2x близко к 1 в типичном случае, когда x близок к 0, поэтому если вычитать 1 отдельно после вычисления 2x, возникает сильная потеря значащих битов из-за вычитания близких величин. С другой стороны, если вам нужно именно 2x, явное прибавление 1 не ухудшает точность. Это пример того, как инструкции с плавающей запятой аккуратно спроектированы для сохранения точности. Подробности — в книге «Руководство по 8087» от архитекторов процессора 8086 и сопроцессора 8087. 

12. У Pentium есть инструкции логарифма по основанию 2: FYL2X и FYL2XP1. Инструкция FYL2X вычисляет y · log2(x), а FYL2XP1 — y · log2(x+1). Инструкции включают умножение, потому что при большинстве операций с логарифмами требуется умножение для смены основания; выполнение умножения с внутренней точностью повышает точность результата. Вариант «плюс один» улучшает точность для аргументов, близких к 1, например в расчётах процентов.

Моя гипотеза по приведению диапазона такова: входной аргумент масштабируется в интервал от 1 до 2. (Взять логарифм от показательной части аргумента тривиально, поскольку логарифм по основанию 2 от степени двойки — это просто показатель степени.) Далее аргумент делится на наибольшую константу вида 1 + n/64, меньшую аргумента. Это сведёт аргумент к диапазону [1, 1+1/32]. Полином логарифма можно вычислять на приведённом аргументе. Наконец, добавляется константа из ПЗУ для log2(1 + n/64), чтобы компенсировать деление. Константа разбивается на две части для большей точности.

Мне потребовалось много времени, чтобы понять константы логарифма, потому что они были разделены. Константы «верхней части» казались бессмысленно неточными, так как нижние 27 бит обнулены. Константы «нижней части» выглядели как крошечные полуслучайные числа порядка ±10-13. В конце концов я понял, что фокус в их сложении.

Читать другие статьи серии:

  1. Исследование кремниевых кристаллов процессора Intel 386

  2. Невидимая оборона 386: как защищены входы и выходы процессора


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

  • 17 ноября: «Архитектурные решения в Backend-разработке». Записаться

  • 17 ноября: «Основные шаблоны проектирования в системном дизайне». Записаться

  • 24 ноября: «Вход в ядро: системные вызовы и граница между user space и kernel space». Записаться