Реверс-инжиниринг π: как Pentium считал синусы быстрее всех
- пятница, 14 ноября 2025 г. в 00:00:11
В 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 для вычисления аппроксимационного полинома.

В двоичной системе π записывается как 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 — его отсутствием (сплошной тёмный кремний). Каждый увеличенный чёрный прямоугольник внизу содержит два потенциальных транзистора и, соответственно, хранит два бита. Ключевая идея в том, что по рисунку «полосок» можно восстановить расположение транзисторов и, следовательно, значение каждой константы — в данном случае π.

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

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

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

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

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

Часть экспоненты у каждой константы состоит из 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, поэтому драйверы расположены горизонтально по четыре; на фото виден только один.

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

Каждый функциональный узел собран из ячеек — по одной на каждый бит, где слева расположен старший бит, а справа — младший. Все ячейки имеют одинаковую ширину — 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). Например, уравнение ниже задаёт ряд Тейлора для синуса.

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

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

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

Подытожим: ряд Тейлора полезен в математическом анализе, но его не стоит использовать для приближённого вычисления функции. Намного лучшая аппроксимация получается при очень небольшом изменении коэффициентов по алгоритму Ремеза. Это объясняет, почему коэффициенты в ROM «почти, но не совсем» совпадают с рядом Тейлора.
Arctan
Теперь посмотрим на константы Pentium для разных трансцендентных функций. В постоянной памяти содержатся коэффициенты для двух полиномов arctan: один для одинарной точности, другой — для двойной. Эти полиномы почти совпадают с рядом Тейлора, но были изменены ради повышения точности. Кроме того, в ROM хранятся значения arctan(1/32) … arctan(32/32); процесс редукции диапазона использует эти константы вместе с тригонометрическим тождеством, чтобы сузить диапазон аргумента до [−1/64, 1/64].9 примеч.
На графике ниже показана ошибка полинома arctan из Pentium (синий) по сравнению с рядом Тейлора той же длины (оранжевый). Полином Pentium выигрывает благодаря оптимизации по Ремезу. Хотя полином Тейлора заметно более ровный в центре, его ошибка резко растёт у границ. Полином Pentium колеблется сильнее, но удерживает низкую ошибку на всём диапазоне. Вне этого диапазона ошибка у полинома 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 по сравнению с полиномом ряда Тейлора.

Полином обслуживает узкий диапазон аргумента [−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 — «плохая константа с плавающей точкой».
Чтобы проанализировать 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 |
В этой статье я разбираю версию «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-логику также называют 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. В конце концов я понял, что фокус в их сложении.
Читать другие статьи серии:
В заключение приглашаем всех желающих на бесплатные демо-уроки, которые проведут преподаватели курсов в рамках набора на новые потоки. Приходите познакомиться с экспертами и узнать что-то новое:
17 ноября: «Архитектурные решения в Backend-разработке». Записаться
17 ноября: «Основные шаблоны проектирования в системном дизайне». Записаться
24 ноября: «Вход в ядро: системные вызовы и граница между user space и kernel space». Записаться