Навигация с помощью секстанта или маневры с использованием углов подвеса могут быть двумя примерами случаев, когда компьютеру Apollo может потребоваться выполнить тригонометрию.
Тригонометрические функции, такие как синус, арктангенс и т. д., трансцендентны , как и логарифмы. Вы не можете вычислить эти функции с помощью простого выражения, построенного, например, на умножении или делении, по крайней мере, без итеративного алгоритма.
Инженер на земле брал бы логарифмическую линейку для двух или трех цифр, а для большего количества цифр обращался бы к книге триггеров, логарифмических и других таблиц. Между двумя строками вы можете интерполировать вручную еще больше цифр.
Но как компьютеры «Аполлон» оценивали трансцендентные функции или, по крайней мере, как были реализованы в программах расчеты, требующие использования трансцендентных функций?
Поскольку код Аполлона-11 находится на GitHub, мне удалось найти код, который выглядит как реализация функций синуса и косинуса: здесь командный модуль и здесь лунный посадочный модуль (похоже, это один и тот же код).
Для удобства привожу копию кода:
# Page 1102
BLOCK 02
# SINGLE PRECISION SINE AND COSINE
COUNT* $$/INTER
SPCOS AD HALF # ARGUMENTS SCALED AT PI
SPSIN TS TEMK
TCF SPT
CS TEMK
SPT DOUBLE
TS TEMK
TCF POLLEY
XCH TEMK
INDEX TEMK
AD LIMITS
COM
AD TEMK
TS TEMK
TCF POLLEY
TCF ARG90
POLLEY EXTEND
MP TEMK
TS SQ
EXTEND
MP C5/2
AD C3/2
EXTEND
MP SQ
AD C1/2
EXTEND
MP TEMK
DDOUBL
TS TEMK
TC Q
ARG90 INDEX A
CS LIMITS
TC Q # RESULT SCALED AT 1.
Комментарий
# SINGLE PRECISION SINE AND COSINE
указывает, что следующее действительно является реализацией функций синуса и косинуса.
Информацию о типе используемого ассемблера можно найти в Википедии .
Частичное объяснение кода:
Подпрограмма SPSIN
фактически вычисляет
, и SPCOS
вычисляет
.
Подпрограмма SPCOS
сначала добавляет половину ко входу, а затем переходит к вычислению синуса (это верно из-за
). Аргумент удваивается в начале SPT
подпрограммы. Поэтому теперь нам нужно вычислить
за
.
Подпрограмма POLLEY
вычисляет почти полиномиальную аппроксимацию Тейлора
. Во-первых, мы храним
в регистре SQ (где
обозначает вход). Это используется для вычисления многочлена
которые выглядят как первые коэффициенты Тейлора для функции .
Эти значения не являются точными! Так что это полиномиальное приближение, которое очень близко к приближению Тейлора, но даже лучше (см. ниже, также благодаря @uhoh и @zch).
Наконец, команда удваивает результат DDOUBL
, и подпрограмма POLLEY
возвращает приближение к
.
Что касается масштабирования (сначала вдвое, затем вдвое, ...), @Christopher упомянул в комментариях, что 16-битное число с фиксированной точкой может хранить только значения от -1 до +1. Поэтому необходимо масштабирование. См. здесь источник и дополнительные сведения о представлении данных. Подробности инструкций по ассемблеру можно найти на той же странице.
Насколько точно это почти Тейлоровское приближение? Здесь вы можете увидеть график на WolframAlpha для синуса, и он выглядит как хорошее приближение для от к . Здесь показана функция косинуса и ее аппроксимация . (Надеюсь, им никогда не приходилось вычислять косинус для значения , потому что тогда ошибка была бы неприятно большой.)
@uhoh написал код на Python , который сравнивает коэффициенты из кода Аполлона с коэффициентами Тейлора и вычисляет оптимальные коэффициенты (исходя из максимальной ошибки для и квадратичная ошибка в этой области). Это показывает, что коэффициенты Аполлона ближе к оптимальным коэффициентам, чем коэффициенты Тейлора.
На этом графике различия между и отображаются приближения (Аполлон/Тейлор). Видно, что приближение Тейлора гораздо хуже для , но гораздо лучше для . С математической точки зрения это не является большой неожиданностью, потому что тейлоровские приближения определяются только локально и, следовательно, часто полезны только вблизи одной точки (здесь ).
Обратите внимание, что для этой полиномиальной аппроксимации вам нужно всего четыре умножения и два сложения ( MP
и AD
в коде). Для управляющего компьютера Apollo циклы памяти и процессора были доступны только в небольшом количестве.
Есть несколько способов повысить точность и диапазон ввода, которые были бы доступны для них, но это привело бы к большему количеству кода и большему времени вычислений. Например, использование симметрии и периодичности синуса и косинуса, использование разложения Тейлора для косинуса или просто добавление дополнительных членов разложения Тейлора повысило бы точность, а также позволило бы использовать произвольно большие входные значения.
Вы также просили логарифм, так что давайте сделаем и это. В отличие от функций синуса и косинуса, эта не реализуется только с помощью подхода, подобного ряду Тейлора. Алгоритм основан на сдвиге входных данных и подсчете количества сдвигов, необходимых для достижения требуемого масштаба. Я не знаю названия этого алгоритма, этот ответ на SO описывает основной принцип.
Реализация LOG
является частью модуля CGM_GEOMETRY и помечена как
ПОДПРОГРАММА ДЛЯ ВЫЧИСЛЕНИЯ НАТУРАЛЬНОГО ЛОГА
Подпрограмма использует NORM
ассемблерную инструкцию, которая, согласно документации , сдвигает число в аккумуляторе (регистре "MPAC") влево до тех пор, пока не будет получено значение
и "почти
" [1] , и записывает количество выполненных операций сдвига в ячейку памяти, указанную в качестве второго аргумента (математический смысл операции сдвига влево - двоичное возведение в степень
, показатели степени в аргументе логарифма могут быть выражены как множители, а произведения в аргументе могут быть выражены в виде сложений, поэтому упрощение
в
работает, где
это количество смен и
предварительно вычисленное значение
).
Затем он использует полином третьей степени для аппроксимации в этом интервале с жестко заданными коэффициентами [3] :
В конце концов полученный ранее счетчик сдвигов снова умножается (умножается на константу 0,0216608494 [2] , используя SHORTMP
).
Давление оптимизации, должно быть, было настолько велико, что они не исправили перевернутый знак перед возвратом из подпрограммы, вместо этого требуя, чтобы все сайты вызова учитывали это.
Применение подпрограммы логарифмирования:
например, как часть предсказания дальности при повторном входе в атмосферу.
---
[1] формат хранения для числа двойной точности был построен из двух 16-битных слов, где MSB каждого является знаком и формирует представление диапазона с дополнением до единицы но LSB - это бит четности. Таким образом, мы имеем дело с 30-битным форматом, содержащим два знаковых бита, что вызывает некоторую головную боль при реализации эмулятора.
[2] язык ассемблера ACG позволяет CLOG2/32
использовать имя идентификатора. Это вызвало дополнительную головную боль для реализации эмулятора .
[3] как были найдены коэффициенты? Комментарии к коду ассемблерного листа интерпретирующего тригнонометрического модуля (да, астронавты могли заставить ACG интерпретировать динамические инструкции) позволяют предположить, что метод был основан на работах Ч. Гастингса, особенно Approximations for Digital Computers. Издательство Принстонского университета, 1955 г. Самый сложный многочлен такого рода в ACG — это полином седьмого порядка того же модуля для вычисления ).
Одно дополнение к ответу @supinf :
а) Начальный DOUBLE
вSPT
б) переполнения для входа x (регистр A
) выше +0,5 (+90°) и потери значимости для x ниже -0,5 (-90°). В этом случае A
это >+1 или <-1, а следующее TS
сохраняет исправленное значение (фактически добавьте единицу, если оно ниже -1, или вычтите единицу, если оно больше +1) TEMK
и задает A
значение +1 ( переполнение) или -1 (недополнение). TCF
Кроме того , переход POLLEY
игнорируется.
c) XCH
заменяется TEMK
на A
, поэтому теперь A
содержит исправленное значение и TEMK
±1.
г) INDEX
добавляет значение из TEMK
(±1) к значению следующей AD
инструкции, что молча исправляет переполнения. Поскольку LIMITS
равно -0,5, это приводит к прибавлению 0,5 в случае переполнения (-0,5 + +1 = 0,5) и к вычитанию 0,5 в случае недополнения (-0,5 + -1 = -1,5 = -0,5). .
e) инвертирует COM
значение A
- включая инвертирование бита переполнения - и
f) финал AD
добавляет единицу в случае переполнения и вычитает единицу в случае потери значимости. AD
не выполняет коррекцию переполнения перед добавлением и устанавливает флаг переполнения после. Таким образом, каждое переполненное значение (>+135° и <-135°) вернется в диапазон [-1,+1].
Если это AD
переполняется / переполняется (я не вижу, как это могло бы произойти), оно устанавливается A
в ± 1, переходит к ARG90
и устанавливается A
в -( LIMITS
+ A
), что составляет -(-0,5+1)=-(+0,5)=-0,5 в в случае переполнения и -(-0,5-1)=-(-1,5)=-(-0,5)=+0,5 в случае недостаточного переполнения. Сначала я думал, что это произойдет при x > +135° или x < -135°, но, похоже, это не так.
Но такая настройка корпуса <-90° и >+90° мне кажется неправильной. Я ожидаю, что строка f будет от (+0,5,+1,0) до (+1,0,+0,0) и от (-0,5,-1,0) до (-1,0,-0,0). Это было бы так, если бы COM
следовало непосредственно XCH
без шага d.
Пожалуйста, поправьте меня, если я ошибаюсь в некоторых частях, я только недавно видел этот код и пытался понять его с помощью AGC Instruction Set .
Кристоф
СФ.
ооо
history
тег, чтобы сделать это еще яснее. Вы также можете подумать о том, где должны храниться таблицы, поскольку в компьютерах Apollo было очень мало памяти.ооо
MSalters
Рассел Борогов
Нейтронная Звезда
ооо
Уве
Нейтронная Звезда
ооо
ооо
Джейми Ханрахан
ооо
Крис Стрэттон
фуклв
Уве
Уве