Глава 5
ОПЕРАЦИИ НАД ЦЕЛЫМИ ЧИСЛАМИ

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

СОКРАЩЕННЫЕ ОПЕРАЦИИ

Рассмотрим сначала наиболее простые операции. Ниже приводятся слова, смысл которых вам очевиден1:

1+ ( n -- n+1 ) Добавление единицы.
1- ( n -- n-1 ) Вычитание единицы.
2+ ( n -- n+2 ) Добавление двойки. 
2- ( n -- n-2 ) Вычитание двойки.  
2* ( n -- n*2 ) Умножение на два (арифметический сдвиг влево).
2/ ( n -- n/2 ) Деление на два (арифметический сдвиг вправо).

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

1 Для начинающих. Понятие «арифметический сдвиг влево (вправо)» мы объясним позднее

СМЕШАННЫЕ МАТЕМАТИЧЕСКИЕ ОПЕРАЦИИ

Приведем еще четыре математические операции. Как и в случае сокращенных операций, их функции должны быть вам понятны:

ABS    ( n -- |n| ) Помещение в стек абсолютной величины заданного числа.
NEGATE  ( n -- -n ) Изменение знака на противоположный.
MIN ( n1 n2 -- min) Помещение в стек минимального из двух заданных чисел.
МАХ ( n1 n2 -- max) Помещение в стек максимального из двух заданных чисел.

Рассмотрим две простые задачи на использование слов ABS и MIN.

ABS

Напишите определение для вычисления разности между двумя числами (независимо от порядка их следования):

: РАЗНОСТЬ ( n1 n2 -- разность ) - ABS ;

Результаты получаются одинаковыми при любом порядке ввода:

52 37 РАЗНОСТЬ . 15 ok или
37 52 РАЗНОСТЬ . 15 ok

MIN

Напишите определение для вычисления суммы комиссионного сбора, которую получат продавцы мебели, если им обещано 50 дол. или 1/10 часть от продажной цены при каждой сделке (в зависимости от того, какая из сумм окажется меньшей):

: КОМИССИОННЫЕ ( цена - комиссионные ) 10 / 50 MIN ;

При трех различных значениях получается следующее.

600 КОМИССИОННЫЕ . 50 ok
450 КОМИССИОННЫЕ . 45 ok
50  КОМИССИОННЫЕ . 5 ok

СТЕК ВОЗВРАТОВ

Ранее мы рассмотрели несколько операций со стеком. Однако существует еще целый ряд операций, с которыми, вам предстоит познакомиться. До сих пор речь шла лишь о конкретном стеке так, как если бы он был один. Но на самом деле их два: стек данных и стек возвратов. Стек данных используется при программировании на Форте чаще, поэтому в тех случаях, когда это не вызывает недоразумений, он называется просто стеком.

Как вы уже видели, в стеке данных хранятся параметры (или аргументы), передаваемые от слова к слову. Стек же возвратов содержит любое число указателей, используемых Форт-системой для нахождения оптимального пути в лабиринте слов, которые вызывают выполнение других слов. Подробнее этот вопрос мы обсудим позднее.

Пользователь может рассматривать стек возвратов в качестве некоторого рода «дополнительной руки», которая «держит» значения в то время, когда вы работаете со стеком данных.

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

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

Ниже дается перечень слов, связанных со стеком возвратов. Помните, что описание состояния стека относится здесь к стеку данных.

>R ( n -- )  Выборка значения из стека данных 
             и занесение его в стек возвратов.
R> ( -- n )  Выборка значения из стека возвратов
             и занесение его в стек данных.
R@ ( -- n )  Копирование содержимого вершины стека
             возвратов без изменения его значения.

Слова >R и R> перемещают элемент стека данных в стек возвратов и элемент стека возвратов в стек данных соответственно. Показанные выше забавные рисунки иллюстрируют операции со стеком в состоянии

(2 3 1 -- 3 2 1),

 заданные выражением

>R SWAP R>

Каждое слово >R и соответствующее ему слово R> должны выполняться совместно в одном и том же определении или, если они выполняются в диалоговом режиме, в одной и той же вводимой строке (прежде чем вы нажмете клавишу RETURN).

Слово R@ только копирует значение из стека возвратов, но не удаляет его. Так что, введя выражение

>R SWAP R@

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

Поясним изложенное на примере. Допустим, вам настолько не повезло, что приходится вычислять значение полинома ах2 + bx+с, причем задаваемые величины хранятся в стеке в следующем порядке:

( а b с х -- )

(Напоминаем, что операция возведения в степень должна выполняться первой.)

ОПЕРАЦИЯ      СТЕК ДАННЫХ         СТЕК ВОЗВРАТОВ
              а b с х
>R            a b с               х
SWAP ROT      с b а               х
R@            с b а х             х
*             с b ах              х
+             с (ax+b)            х
R> *          с x(ax+b)
+             x(ax+b)+c

Попытаемся вычислить его. Загрузите следующее определение:

: ПОЛИНОМ ( a b c x -- n)
   >R SWAP ROT R@ * + R> * + ;

Теперь проверим его:

2 7 9 3 ПОЛИНОМ 48 ok

АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
НАД ЧИСЛАМИ С ПЛАВАЮЩЕЙ ТОЧКОЙ

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

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

Десятичная точка «плавает» по индикатору по мере необходимости. Такой индикатор называется индикатором с плавающей точкой. Представление с плавающей точкой - это способ записи чисел в компьютере в виде мантиссы и порядка. Например, 12 млн. можно записать как 12x106, поскольку 106 равно 1 млн. Во многих компьютерах 12 млн. должно быть записано в виде двух чисел 12 и 6, что воспринимается как 106, умноженное на 12. Число 3.345 будет записано так: 3345 и -3.

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

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

Сравним для иллюстрации три представления чисел: общепринятое, с фиксированной точкой и с плавающей точкой:

ОБЩЕПРИНЯТОЕ     С ФИКСИРОВАННОЙ    С ПЛАВАЮЩЕЙ
ПРЕДСТАВЛЕНИЕ    ТОЧКОЙ             ТОЧКОЙ
    1.23           123               123(-2)
   10.98          1098              1098(-2)
  100.00         10000                 1( 2)
   58.60          5860               586(-1)

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

ПОЧЕМУ ПРОГРАММИСТЫ ПРЕДПОЧИТАЮТ МАСШТАБИРОВАНИЕ

Многие опытные программисты, использующие традиционные языки программирования, воспринимают представление с плавающей точкой как нечто само собой разумеющееся. Их мнение можно выразить примерно так: «Почему я должен следить за перемещением десятичной точки? Для чего же тогда нужны компьютеры?». Вопрос поставлен правильно - он отражает основное преимущество реализации арифметических операций над числами с плавающей точкой. При переводе математических уравнений в машинный код такое представление чисел существенно облегчает жизнь программисту.

Однако многие прикладные Форт-программы должны работать в реальном масштабе времени. При этом компьютер используется для управления некоторым устройством или организации функционирования ряда дисплеев и клавиатур. Такие программы, для того чтобы «выжимать» из устройств максимальную производительность, нужно делать по возможности быстродействующими Следовательно, программист зачастую заинтересован в максимизации производительности аппаратных средств в большей степени, чем в повышении эффективности программирования. Во многих случаях (например, при использовании карманных компьютеров) к тому же приходится экономить память.

Если в вашей программе некоторые вычисления должны повторяться миллионы раз, то требуемую скорость вы получите, выполняя арифметические операции над числами с фиксированной точкой. Действительно ли велик получаемый при этом выигрыш? Бесспорно. Время выполнения операций деления или умножения чисел с плавающей точкой намного превосходит время выполнения аналогичных операций над числами с фиксированной точкой. А при сложении или вычитании на подготовку и преобразование аргументов уходит столько же времени, сколько и на саму операцию. Большинство мини- и микрокомпьютеров «не думает» в терминах представлений с плавающей точкой и накладные расходы по организации выполнения соответствующих операций на них очень велики.

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

Неверно, что Форт не имеет возможности поддерживать арифметику с плавающей точкой. Программистами созданы на Форте функции с плавающей точкой [1] - [7], а в ряде Форт-систем применяются сопроцессоры с плавающей точкой (отдельный чип, единственной функцией которого является реализация высокоскоростных операций над числами с плавающей точкой) [8], [9].

Большинство программистов считают, что операции над числа ми с плавающей точкой им необходимы, но это не так При решении многих физических задач значения обрабатываемых и получаемых величин не выходят за пределы диапазона от единицы до нескольких тысяч и, следовательно, умещаются в 16-разрядное целое слово. (Некоторые вычисления могут потребовать 32-разрядного представления, что Форт в состоянии обеспечить.) К таким задачам относится моделирование погоды, восстановление изображения, автоматическое измерение электрических цепей и т. п. Масштабирование дает вам возможность работать с данными в 16-разрядном целочисленном диапазоне и поэтому отпадает надобность в расточительных операциях над числами с плавающей точкой. Не подумайте, что мы вынуждаем вас нарушать общепринятые правила и оставляем наедине со своими проблемами. Форт представляет в ваше распоряжение уникальный набор команд, так называемых операций масштабирования, которые поддерживают аппроксимацию вещественных величин и выполнение операций с дробными значениями. В следующем разделе мы познакомим вас с основными возможностями целочисленной арифметики

ОПЕРАЦИЯ МАСШТАБИРОВАНИЯ */

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

*/ ( n1 n2 n3 --         Умножение, затем деление (n1*n2/nЗ).
            результат )  Промежуточный результат 32-разрядный.

Например, допустим, что в стеке содержатся три числа*

( 225 32 100 -- )

*/ сначала перемножит 225 на 32, а затем разделит полученный результат на 100. Эта операция особенно удобна для решения таких задач, как вычисление процентов. В частности, вы можете определить слово % следующим образом:

: % ( n % - n') 100 */ ; 

Введя число 225, а затем выражение

32 %

вы получите в стеке 32% от 225, т. е. 721.

Команда */ это не простое сочетание команд * и /. Она использует для промежуточного результата число удвоенной длины. «Что это значит?», - спросите вы. Предположим, вам нужно вычислить 34% от 2000. Вспомним, что операции над числами одинар-

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

ной точности, такие, как * и /, дают результаты в диапазоне от -32768 до +32767. Если вы введете выражение

2000 34 * 100 /

(то получите неверный результат, поскольку промежуточное выражение (в нашем случае произведение) превышает 32767:

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

2800 34 100 */

дает правильный результат, так как конечное выражение находится в пределах диапазона одинарного представления чисел.

ОКРУГЛЕНИЕ

Из предыдущего примера вытекает другой вопрос: как производить округление? Решим такую задачу: сколько бананов нужно завезти в школьный буфет, если он рассчитан на 225 человек, и 32% из них обычно покупают бананы? Естественно, нас интересуют только целые бананы, так что в случае получения нецелого результата его нужно округлить. Сейчс слово % определено так, что все цифры справа от десятичной точки просто отбрасываются. Иными словами, результат «усекается»:

32% ОТ          РЕЗУЛЬТАТ
225 = 72.00     72 - абсолютно правильный
226 = 72.32     72 - правильный, округлен в меньшую сторону (усечен)
227 = 72.64     72 - усечен, но не округлен

Существует, однако, способ, при котором любое десятичное значение от 0,5 и выше, полученное в остатке, округляется до следующего целого. Для того чтобы найти «округленные проценты», мы должны определить слово R%:

: R% ( n % -- результат ) 10 */ 5 + 10 / ;

так что теперь выражение

227 32 R% .

даст в результате 73, т. е. правильно округленное до следующего целого числа.

Заметьте, что сначала мы делим на 10, а не на 100. При этом у нас появляется возможность добавить цифру 5 в позицию единиц числа, полученного в результате выполнения операции */.

ОПЕРАЦИЯ      СОДЕРЖИМОЕ СТЕКА
              227 32 10
*/            726
5 +           731
10 /          73

Окончательное деление на 10 приводит число к надлежащему виду, в чем вы можете убедиться самостоятельно1.

Как недостаток такого способа округления необходимо отметить, что вы теряете одну значащую цифру в конечном результате, а именно вместо 32,767 мы можем получить только 3,276 Но если для вас это важно, вы всегда можете воспользоваться числами двойной длины (они будут рассмотрены позднее), которые также можно округлять.

ВОЗМОЖНОСТИ МАСШТАБИРОВАНИЯ

Решим простую задачу, допустим, что нужно вычислить 2/з от 171. По существу, есть два способа нахождения результата:

1 Для специалистов Существует и более быстрое определение

: R% ( n % -- результат ) 50 */ 1+ 2/ ;

1. Определить значение дроби 2/3 путем деления числа 2 на число 3. При этом мы получим периодическую десятичную дробь, .666666... Далее можно умножить полученное значение на 171. Результат составит 113.9999999 и т. д., что не совсем точно, но он может быть округлен до 114.

2. Перемножить 171 и 2, а затем разделить полученное число 342 на 3, что дает в результате 114.

Заметим, что второй способ проще и намного точнее

Большинство машинных языков поддерживает первый способ. В компьютере не могут храниться такие дроби, как 2/3, на всякий случай. Их нужно выражать в виде десятичной дроби, например .666666...

Форт поддерживает второй способ. Операция */ позволяет вам получать дроби, аналогичные 2/3, как в следующем примере:

171 2 3 */

Теперь, когда вы имеете некоторое представление о масштабировании, рассмотрим несколько более сложный пример. Допустим, требуется разделить 150 дол. в заданной пропорции :

7.105    ?
5.145    ?
------  ---
12.250  150

Эту задачу можно решить так:

(7.105/12.250) х 150 и 
(5.145/12.250) х 150

Однако для обеспечения большей точности мы должны написать

(7.105 х 150)/12.250 и
(5.145 х 150)/12.250

На Форте это будет выглядеть следующим образом:

   7105 150 12250 */ . 87 ok

и

   5145 150 12250 */ . 63 ok

Мы можем сказать, что значения 87 и 63 выбраны в «масштабе» к 7105 и 5145. Вычисление процентов, выполненное нами ранее, также представляет собой форму масштабирования. По этой причине операция */ называется операцией масштабирования.

Еще одной операцией масштабирования является операция */MOD:

*/MOD ( n1 n2 nЗ --           Умножение, затем деление (n1*n2/n3).
    n-остаток n-результат )   Помещение на стек остатка и частного.
                              Для промежуточного результата используется
                              слово двойной длины.

Самостоятельно придумайте хороший пример на выполнение операции */MOD

АППРОКСИМАЦИЯ ВЕЩЕСТВЕННЫХ ЧИСЕЛ

До сих пор мы применяли масштабирование для выполнения операции над вещественными числами. Масштабирование также может использоваться для представления иррациональных констант вещественными значениями, например числа π или √2. Фактически значение π представляется дробью 3.14159265358. Но для того, чтобы оставаться в пределах арифметики с числами одинарной длины, мы должны записать эту дробь в виде выражения

31416 10000 */
что дает довольно хорошее приближение.

Теперь мы можем вычислить площадь круга по заданному радиусу. Переведем формулу πr2 на язык Форт. Значение радиуса должно находиться в стеке, поэтому удвоим его с помощью DUP и умножим на самого себя, а затем получим конечный результат, применив операцию */.

: PI ( n - n') 31416 10000 */ ;
: ПЛОЩАДЬ ( радиус - площадь) DUP * PI ;

С помощью определения вычислим площадь круга с радиусом 10 дюймов:

10 ПЛОЩАДЬ . 314 ok

Для получения еще большей точности мы могли бы поискать другую пару чисел, которая давала бы лучшее приближение. Как ни странно, такая пара есть. Это дробь

355 113 */

обеспечивающая точность, большую, чем шесть знаков после запятой, в то время как 31416 - обеспечивает менее четырех знаков

Следовательно, наше новое, улучшенное определение будет иметь вид:

: PI ( n -- n') 355 113 */ ;

Оказывается, вы можете хорошо аппроксимировать любую константу посредством множества различных пар целых чисел, значения которых меньше, чем 32768, с погрешностью менее 10-8'.

ОПЕРАЦИИ НАД ДРОБНЫМИ ЧИСЛАМИ

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

 7      23
---- + ---- =
 34     99
На Форте вы можете сделать это с помощью команд так называемой дробной арифметики, реализующих операции над числами с фиксированной точкой.

При использовании дробной арифметики мы применяем масштабирование с подразумеваемым положением десятичной точки. Но вместо масштабирования посредством умножения на степень 10 (как принято при вычислениях вручную) мы будем масштабировать путем умножения на степень 2 (как принято при вычислении на компьютере). Таким образом, выражение «десятичная точка» уместно заменить выражением «двоичная точка»"

1 Для истинных математиков Ниже приводится небольшая таблица вещественной аппроксимации различных констант:

    Значения                        Аппроксимация     Погрешность
                 π = 3.141 ...         355 /  113      8.5 x 10-8
                √2 = 1.414 ...       19601 /13860      1.5 x 10-9
                √3 = 1.732 ...       18817 /10864      1.1 x 10-9
                 e = 2.718 ...       28667 /10546      5.5 x 10-9
               √10 = 3.162 ...       22936 / 7253      5.7 x 10-9
              12√2 = 1.059 ...       26797 /25293      1.0 x 10-9
     log102/1.6384 = 0.183 ...        2040 /11103      1.1 x 10-8
        ln2/16.384 = 0.042 ...         485 /11464      1.0 x 10-7
  .001°/22-bit rev = 0.858 ...       18118 /21109      1.4 x 10-9
arc-sec/22-bit rev = 0.309 ...        9118 /29509      1.0 x 10-9
                 c = 2.9979248 ...   24559 / 8192      1.6 x 10-9

2 Для начинающих Этот вопрос подробнее рассматривается в гл 7

Допустим, вы ввели следующее определение:

: +1 ( -- масштабная-единица ) 16364 ;

Здесь выбран масштаб» при котором число 16384 представляет положительную единицу (константы будут введены в гл. 8). В двоичной системе счисления число 16384 выглядит следующим образом: 0100000000000000

т. е. это единица в соответствующем масштабе с подразумевае-мой двоичной точкой.

Теперь добавьте к Форту две новые арифметические операции: дробное умножение и дробное деление, выполняемые с учетом выбранного нами масштаба.

: *. ( n n -- n) +1 */ ;
: /. ( n n -- n) +1 SWAP */ ;

Что же у вас получилось? При делении единицы на единицу должна получиться единица.

1 1 /. . 16384 ok

(Напомним, что в выбранном масштабе 16384 это единица.) Теперь разделите 1 на 2:

1 2 /. . 8192 ok

Здесь 8192 представляет половину единицы (половина от 16384). Следовательно, вы можете решить поставленную задачу таким образом:

7 34 /. 23 99 /. +

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

10000 *. . 4381 ok

Ответ составит 4381/10000, или в более привычном виде 0.4381.

С помощью дробной арифметики, используя быстрые операции над целыми числами, вы можете складывать, вычитать, умножать

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

Имеет смысл решать на компьютере задачи теми средствами, к которым он приспособлен, а не навязывать ему образ действий, являющийся следствием нашего школьного образования

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

: #.#### DUP ABS 0 <# # # # # 46 HOLD # ROT SIGH #>
         TYPE SPACE ;
: .F ( дробь -- ) 10000 *. #.#### ;

Теперь наше выражение будет иметь вид

7 34 /. 23 99 /. + .F 0.4381 ok

Это не представление с плавающей точкой, но результат в принципе такой же и получен гораздо быстрее.

Допустим, вы хотите, чтобы вводимые аргументы выглядели как вещественные числа, например:

.1250 + .3750 = ?

Для того чтобы выполнить такую операцию на Форте, в первую очередь вам потребуется слово, преобразующее числа с десятичной точкой в масштабированную дробь. Детали мы объясним вам в гл. 7, а для своих текущих потребностей вы можете определить, например, слово

: D>F ( d -- дробь) DROP 10000 /. ;

(D>F означает перевод из числа двойной длины в дробное. DOUBLE - двойной, FRACTION - дробь). Теперь можно вводить следующие выражения:

.1250 D>F .3750 D>F + .F 0.5000 ok

1 Для пользователей фиг-Форта, систем полиФорт, созданных бо введения Стандарта-83 и других старых систем Перед SIGN слово ROT должно быть опущено

Обратите внимание на то, что необходимо дополнять исходные данные до четырех десятичных знаков.

Вы можете выполнять умножение двух дробей посредством *.

.7500 D>F .5000 D>F *. .F .3750 ok

Интересно отметить, что если вы выполняете операцию умножения *. дроби на целое, то результат будет целым, например:

28 .5000 D>F *. . 14 ok

Применяя операцию /., можно разделить, скажем, -0.3 на 0.95:

-.3000 D>F .9500 D>F /. .F -0.3160 ok

Выполняя ту же операцию над двумя целыми, вы получите дробный результат:

22 44 /. .F .5000 ok

Если же вы делите посредством /. целое на дробь, то в результате получите целое. Обозначив символом f дробь, а символом i целое, мы можем построить следующую таблицу выполнения операций:

ОПЕРАЦИЯ       РЕЗУЛЬТАТ
  f f +            f
  f f -            f
  f i *            f
  i f *            f
  f i /            f
  f f *.           f
  f i *.           i
  i f *.           i
  f f /.           f
  i i /.           i
  i f /.           i

Использование двоично-ориентированного масштаба, например числа 16384, а не десятично-ориентированного, например 10000, позволяет обеспечить в пределах 16 разрядов большую точность (точность повышается в отношении 16 к 10). При этом операции */ и /. кодируются на ассемблере чрезвычайно эффективно. Но и число 16384 мы выбрали в качестве единицы произвольно. Если бы было выбрано число 256, то получилось бы восемь разрядов (включая знак) слева от двоичной точки и восемь разрядов справа. Этот метод при необходимости можно применить и к 32-разрядным числам.

Операции *. и /. можно выполнять над тригонометрическими функциями, поскольку значение угла представляется как функция длины окружности, выраженная дробью (в интервале от 0 до 1). Перевод радиан в градусы и обратное преобразование осуществляются весьма просто.

ЗАКЛЮЧЕНИЕ

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

Как уже упоминалось, вам ничто не мешает дополнительно ввести в Форт операции над числами с плавающей точкой Но такие средства не очень подходят Форту, поскольку его достоинства - это компактность, высокая скорость выполнения программ, простота и элегантность. Он как бы «отторгает» от себя все то, что не является насущной необходимостью. Разумно используя масштабирование и числа двойной длины, вы избавитесь от дорогостоящих операций над числами с плавающей точкой.

Операции над числами с плавающей точкой имеет смысл применять в следующих случаях:

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

Ниже приводится список слов Форта, используемых в данной главе:

1+     ( n -- n+1 )       Добавление единицы.
1-     ( n -- n-1 )       Вычитание единицы.
2+     ( n -- n+2 )       Добавление двойки.
2-     ( n -- n-2 )       Вычитание двойки.
2*     ( n -- n*2 )       Умножение на два (арифметический сдвиг влево).
2/     ( n -- n/2 )       Деление на два (арифметический сдвиг вправо).
ABS    ( n -- |n| )       Помещение в стек абсолютной величины
                          заданного числа.
NEGATE ( n -- -n )        Изменение знака на противоположный.
MIN    ( n1 n2 -- min)    Помещение в стек минимального из двух
                          заданных чисел.
МАХ    ( n1 n2 -- max)    Помещение в стек максимального из двух
>R     ( n -- )           Выборка значения из стека данных и 
                          занесение его в стек возвратов.
R>     ( -- n )           Выборка значения из стека возвратов и
                          занесение его в стек данных.
R@     ( -- n )           Копирование содержимого вершины стека
                          возвратов без изменения его значения.
*/ ( nl n2 nЗ --          Умножение, затем деление (n1*n2/nЗ).
          результат )     Промежуточный результат 32-разрядный.
*/MOD ( n1 n2 nЗ --       Умножение, затем деление (n1*n2/nЗ).
   n-остаток n-результат) Помещение на стек остатка и частного.
                          Для промежуточного результата используется
                          слово двойной длины.

ОСНОВНЫЕ ТЕРМИНЫ

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

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

Дробная арифметика. Арифметика, где можно выражать дроби с помощью целых чисел, а за положением точки следит сам программист.

Масштабирование. Процесс умножения (или деления) какого-то числа в определенной пропорции. То же относится к процессу

умножения (или деления) числа на степень десяти так, чтобы все значения в некотором наборе данных могли бы быть представлены как целые в предположении, что десятичная точка находится в одной и той же позиции для всех чисел, или на степень 2 в случае дробной арифметики,

Промежуточный результат двойной точности (длины). Значение двойной длины временно создается двухшаговой операцией, такой, как */, тогда, когда промежуточное значение (результат первой операции) может превышать число одинарной длины, даже в том случае, если исходные аргументы и конечный результат не выходят за пределы одинарной точности.

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

Стек, возвратов. В Форте - это участок памяти, который Форт-система использует для хранения «адресов возврата» в отличие от других объектов (будет рассмотрен в гл. 9). Пользователь имеет право хранить значения в стеке возвратов временно, при определенных условиях.

УПРАЖНЕНИЯ

5.1. Укажите различия между -1 и 1-.

5.2 Переведите следующее алгебраическое выражение в форму определения Форта:

  -(ab)/c

при состоянии стека ( а b с --).

5.3. При состоянии стека 

(6 70 123 45 --)

напишите выражение, которое бы инициировало печать наибольшего из этих значений.

5.4. а) Определите слово 2ПОРЯДОК, которое из имеющихся в стеке двух чисел располагало бы в вершине большее, а оставшееся - под ним.

б) Определите слово ЗПОРЯДОК, которое располагало бы три заданных числа в стеке так, чтобы большее было в его вершине.

в) Вспомните определение ОБЪЕМ из гл. 4. Перепишите его, используя определение ЗПОРЯДОК, так, чтобы пользователь мог вводить измерения в любом порядке.

Практикум в масштабировании

5.5. Гистограмма - это графическое представление серии значений, каждая из которых выражена высотой или длиной некоторого отрезка. Определите слово с именем РИСУЙ - компонент вашей программы по созданию гистограмм По заданному значению в диапазоне от 0 до 100 слово РИСУЙ должно вывести на экран горизонтальную линию из звездочек, графически представляющую это заданное значение.

Трудность заключается в том, что на экране только 80 колонок Таким образом, значение 100 должно соответствовать 80 звездочкам, значение 50 - 40 звездочкам, значение 0 - 0 звездочкам и т. д. (Начинайте ваше определение с команды CR и используйте вариант слова STARS из упр. 4 7 )

5.6. В режиме калькулятора переведите указанные значения температур из одной шкалы в другую по формулам

°C=(°F-32)/1.8;
°F = (°C x 1.8) + 32;
°K = °C + 273.

Выразите все аргументы и результаты целыми числами (в градусах):

а) 0°F в °С,

б) 212° F в °С;

в) -32° F в °С;

г) 16° С в °F,

д) 233° К в °С.

5.7. Определите слова для выполнения преобразований из упр. 5.3. Используйте следующие имена:

F>C Р>K C>F C>K K>F K>C

Проверьте их выполнение с приведенными выше значениями.

ЛИТЕРАТУРА

1. Bowhill, Sidney A., "A Variable-Precision Floatmg-Point System for Forth," 1983 FORML Conference Proceedings, Asilomar, California.

2. Bumgarner, John O., and Jonathan R. Sand, "Dysan IEEE P-754 Binary Floating Point Architecture," 1983 Rochester Forth Conference Proceedings, pp. 185-94.

3. Jesch, Michael, "Floating Point in FORTH?" 1981 FORML Conference Proceedings, pp. 61-78; reprinted as "Floating Point FORTH?", Forth Dimensions, 4/1, May-June 1982, pp. 23-25.

4. Harwood, James V., "FORTH Floating Point," 1981 Rochester Forth Standards Conference Proceedings, p. 189,

5. Monroe, Alfred J., "Forth Floating-Point Package," Dr. Dobb's Journal, 7/9, September 1982, pp. 16-29.

6. Petersen, Joel V., and Michael Lennon, "NIC-FORTH and Floating Point Arithmetic," 1981 Rochester Forth Standards Conference Proceedings, pp. 213-17.

7. Duncan. Ray, and Martin Tracy, 'The FVG Standard Floating-Point Extension," Dr. Dobb's Journal, 9/9, September 1984, pp. 110-15.

8. Redington, Dana, "Forth and Numeric Co-processors: An Extensible Way to Floatingpoint Computation," Conference Proceedings of the Eighth WCCF, 3, pp. 368-73, March 18-20, 1983.

9. Redington, Dana, "Stack-Oriented Co-Processors and Forth," Forth Dimensions, 5/3 September-October 1983, pp. 20-22.