Еще об арифметических операциях
Мы уже не раз говорили о том, что компьютеры используются не только для операций с числами, но и во многих других областях, однако, что бы они ни делали, они имеют дело с числами. Эта большая глава представляет собой что-то вроде попурри на Тему чисел. Однако если в предыдущих и большинстве следующих глав для любой серьезной работы по программированию был нужен весь материал, то некоторые части этой главы потребуются вам только в том случае, если вы будете заниматься "перемалыванием" чисел, например числовыми базами данных. Как минимум, вы должны изучить данное введение и разделы "Операторы для работы с небольшими числами", "Некоторые проблемы операции деления", "Операции с величинами и знаками чисел". Если вы работаете в стандарте Форт83, вам нужно также прочитать раздел "Деление с округлением,. деление с отрицательными числами". Если вы будете работать с очень большими числами и дробями, - вам нужно изучить разделы "Почему используются целые числа?", "Масштабирование чисел", "Числа двойной длины" и "Совместное применение чисел одинарной и двойной длины". А если вас интересует работа с числами с плавающей запятой, вы должны прочитать последний раздел. Если вы совершенно устали от арифметики, переходите к гл. 5 или даже гл. 6; для их понимания вам достаточно того, что вы уже узнали, но потом вам все равно придется вернуться, а потому наш совет - не пропускайте материал этой главы. Наконец, чтобы считать себя всесторонне образованным программистом на языке Форт. вам по требуется узнать все, что изложено в этой главе.
К настоящему времени вы изучили четыре арифметические операции: сложение, вычитание, умножение и деление и соответствующие слова Форта +, -, * и /. Нам остается мало что добавить, кроме как упомянуть о потенциально возможной проблеме арифметического переполнения. Предположим, вы складываете числа 36000 и 37000. Результат должен равняться 73000; т.е. быть больше, чем 16- разрядное число, которое может быть в стеке.
На IBM PC на выполнение 1 миллиона операций 2/ MMSFORTH расходует 22 с, а на миллион операций 2 / 80 с. Поскольку действия с числами 1 и 2 встречаются очень часто, особенно при повторяющихся операциях, использование этих слов может значительно увеличить скорость работы программ. Может быть, это и мелочь, однако это хороший пример того, как Форт оптимизирует быстродействие.
Некоторые проблемы операции деления
Предположим, что вы хотите умножить число 20000 на 5 и разделить произведение на 2, при этом должно получиться 50000. Попробуйте это сделать, введя 20000 5 * 2 / U. 42 и то, что вы увидите, будет наверняка неправильно. При умножении происходит переполнение и заведомо неверный результат умножения точно делится на 2. Но эту задачу можно решить иначе: 20000 2 / 5 * ведь не всегда же вы сможете узнать, будут ли расположены большие и малые числа в правильной поcледовательности. Слово */ разрешает эту проблему, запоминая промежуточный результат в виде 32-разрядного числа, а не 16-разрядного, как обычно, допуская значение произведения до 4271406735. Таким образом устраняется возможность переполнения.
Еще одна проблема, возникающая при делении, - это проблема остатка. При целочисленных операциях сложения, вычитания и умножения результат представляет собой другое целое число. Для деления это не так. Например, если 3 поделить на 2, должно получиться 1.5 = 1 + 5/10. Нам нужно каким-либо образом определить остаток от деления (который иногда называется модулем деления). Остаток находится с помощью слова MOD. Если вы введете 3 2 MOD . то на экране получите 1. 25 7 MOD . дает в результате 4. Другими словами, слово MOD выдает остаток от деления двух чисел, или модуль. Есть еще два других слова, которые дают в результате остаток. Одно из них, /MOD, дает остаток от деления двух чисел и частное, которое помещается поверх остатка. Таким образом, 5 3 /MOD . . дает в результате пару чисел 1 и 2, в то время как 25 7 /MOD . . выдает в результате 3 4. Слово */MOD связано с обеими операциями */ и MOD. 332 */MOD . .
приводит к результату 4 1. Второе и третье число в стеке (в данном случае 3 и 3) перемножаются, образуя 32- разрядный результат, чтобы избежать переполнения, затем делится на число, находящееся на вершине стека, остаток от деления остается в стеке, поверх него на вершине стека находится частное. Назначение слова */MOD такое же, как и */, т.е. избежать переполнения во время операции умножения.
Деление с округлением, деление с отрицательными числами
В школе вас учили, что, если при делении двух чисел возникает остаток, результат нужно округлить до ближайшего целого в сторону уменьшения (вниз), т.е. 3/2 дает в частном 1 и в остатке 1. Ну а что делать, если либо делимое, либо делитель отрицательные? По интуиции вы, возможно, считаете, что, например, при делении -6/4 частное будет -1, а какой будет остаток? Вы, если хотите, можете умножить частное на делитель и сложить с остатком, чтобы получить делимое. Таким образом, в данном случае остаток будет равен -2. Потому что -2 + -1 х 4 равно -6. Именно так устроено деление в Форт-79. Для подтверждения проверим на Форт-79: -6 4 / . при этом получается -1, в результате операций -6 4 MOD . получается -2 и после -6 4 /MOD . . получается -1 -2. Для стандарта Форт-79 можно сформулировать правила "интуитивного" деления: 1) если либо делитель, либо делимое, но не одновременно отрицательные, то частное также отрицательное, 2) частное независимо от знака округляется в сторону, ближайшую к 0; 3) остаток принимает знак делимого, или второго числа в стеке.
Пользуясь этими правилами, мы всегда получаем результаты, приведенные в последних примерах. Эти правила справедливы для большинства версий Форта, за исключением тех, которые основаны на стандарте Форт-83. Точное значение результата операции -6/4 равно -1.5. Согласно школьным правилам надо округлить это число в сторону уменьшения, и мы получили бы -2 (-2 меньше, чем -1). Это так называемое деление с округлением по нижней границе (по "полу"). Его суть в том, что за частное от деления принимается ближайшее меньшее значение.
Частное всегда округляется до нижней границы. А как определить остаток? Вы снова можете умножить частное на делитель и добавить остаток, чтобы получить исходное число. Частное равно -2, делитель 4, их произведение равно -8. Чтобы получить -6 после прибавления к этому произведению остатка, он должен быть равен 2. Сформулируем правила для деления с округлением по нижней границе. 1) если либо делитель, либо делимое, но не оба сразу отрицательные, то частное также отрицательное (точно так же, как для "интуитивного" деления); 2) частное независимо от его знака округляется в сторону ближайшего меньшего числа; 3) остаток от деления принимает знак делителя (в противоположность "интуитивному" делению).
В Форт-83 применяется деление с округлением по нижней границе, что является полной противоположностью Форт-79, в связи с чем нужно быть внимательным при переносе программ из одного стандарта в другой. Хотя большинство машинных языков программирования работают как Форт-79 (за исключением АПЛ), с математической точки зрения более корректно округление по нижней границе, т.е. независимо от знака в сторону нижней границы. Тем не менее многие считают, что это противоречит здравому смыслу. Если вас это смущает, может быть, вам помогут несколько примеров, приведенных ниже. Обычное деление на 0 приводит к бесконечному результату, т.е. к разрыву в нуле. Это может привести к сложностям в графике и других применениях, например для робототехники, где необходимо обеспечить плавный переход от положительных к отрицательным числам. Деление с округлением по нижней границе в связи с этим предпочтительнее, но нужно иметь в виду, что оно занимает времени немного больше.
Обычное деление Деление с округлением по нижней ("интуитивное") границе 4 2 / 2 ok 4 2 / . 2 Ok 3 2 / 1 ok 3 2 / . 1 ok 2 2 / 1 ok 2 2 / . 1 ok 1 2 / 0 ok 1 2 / . 0 ok 0 2 / 0 ok 0 2 / . 0 ok -1 2 / 0 ok -1 2 / .-1 ok -2 2 / -1 ok -2 2 / .-1 ok -3 2 / -1 ok -3 2 / .-2 ok -4 2 / -2 ok -4 2 / ,-2 ok
В связи с необходимостью преобразования программ, написанных на Форт-79, в стандарт Форт-83 нужно иметь правила или алгоритм преобразования результата обычного деления в результат деления с округлением по нижней границе. Проще всего представить этот алгоритм, определив два слова на языке Форт. На Форт-79 слово : FL/ /MOD SWAP IF DUP 0 < IF 1- THEN THEN ; будет выдавать частное с округлением по нижней границе при делении чисел в соответствии со стандартом Форт-79- Вам еще не приходилось обращаться с некоторыми словами, использованными в данном определении, но их нетрудно понять. Выражение /MOD SWAP выдает частное и остаток и переставляет остаток на вершину. Если слово IF обнаруживает положительное число, т.е. остаток не равен 0, исполняются слова, которые следуют за IF до второго оператора THEN. В этом случае частное дублируется на вершине стека и сравнивается с нулем с помощью оператора
Освоив стековые манипуляции в гл. 2, вы можете заметить, что частное и остаток деления с округлением по нижней границе можно найти по аналогии со словом /MOD с помощью : FL/MOD 2DUP FLMOD ROT ROT FL/ ;
Следует подчеркнуть, что данные определения работают очень медленно по сравнению с теми определениями на языке ассемблера, которые введены в Форт-83. И ими нужно пользоваться только в случае крайней необходимости.
Упражнения
1. Проделайте в уме следующие примеры, используя компьютер только для проверки ответов. Найдите остаток и частное при "обычном" делении и делении с округлением по нижней границе: (а) 10/2 (б) 0/2 (в) 11/3 (г) -11/3 (д) 11/-3 (е) -11/-3 (ж) -10/2 (з) 10/-2 (и) -10/-2 (к) -10/0 2. Имеется текст из нескольких сотен литер, расположенный в строчках по 50 символов в строке. Напишите слово для вычисления номера строки, в которой находится 112-я литера. (Не забудьте, что вы работаете в целочисленной арифметике.) 3. Используя слово MOD, определите слово для вычисления количества символов, предшествующих 112-му символу в той строке, где он находится. 4.
Используйте идеи из упражнений 2 и 3 и напишите слово для определения номера строки и номера в строке любого символа в тексте из строчек фиксированной длины. При вызове слова в стеке должны находиться номер символа и длина строки, Приемы, которые вы применили в упражнениях 2-4, пригодятся впоследствии, когда вам потребуется определить местонахождение данных, хранящихся в блоках форта, а именно эти приемы помогут вам найти номер блока, в котором находится конкретный фрагмент данных и число байтов смещения внутри этого блока. 5. Напишите определение слова MOD (под именем NEWMOD) для Форт79, используя стандарт Форт-83- В этом процессе вам нужно будет дать определение деления / в Форт-79. 6. Найдите значение выражения 2000*100/30. Проделайте это двумя способами, избегая в обоих случаях переполнения. В первом используйте */, в другом - /MOD- Теперь проделайте то же, чтобы в результате выдавалось значение частного и остатка. Можете ли вы сделать это также двумя способами? Дальше мы узнаем, как использовать остаток, который получается со словом MOD для выполнения арифметических операций с имитацией плавающей запятой.
Операции с величинами и знаками чисел
Каждое из слов МАХ и MIN сравнивают два числа и оставляют в стеке соответственно большее. или меньшее из них. Так, например, 7 3 МАХ оставляет в стеке 7, в то время как 7 3 MIN возвращает в стек 3. Имеется много применений этих слов, некоторые из них мы увидим в упражнениях. Одно из наиболее частых применений - для ограничения диапазона чисел, поступающих входе. Предположим, что вы хотите ограничить диапазон чисел значениями от 5 до 25. Это может быть сделано следующим словом: : LIMIT-RANGE 5 МАХ 25 MIN :
Если встречается число больше, 25, то в стек помещается число 25, в то же время, когда число меньше 5, в стек помещается 5. При определении слов FL/ и FLMOD вы уже видели, как можно сравнить числа, и использовали для этого конструкцию IF...THEN, и вам, вероятно, нетрудно представить, что МАХ и MIN можно определить таким же способом.
Однако такое определение будет очень медленным. Многие программисты стремятся чаще использовать конструкцию IF...THEN, избегая применения слов МАХ и MIN. Но это их ошибка, поскольку МАХ и MIN позволяют значительно ускорить исполнение программы.
Слова ABS и NEGATE воздействуют на знак чисел. (Со словом NEGATE вы встречались в гл. 3.) Слово ABS возвращает в стек абсолютную величину числа. Так, например, -5 ABS возвращает в стек 5, а 5 ABS выдает в стек то же самое число 5.
В свою очередь, слово NEGATE всегда изменяет знак числа. Т.е. -5 NEGATE положит в стек 5, в то время как 5 NEGATE вернет в стек -5. Более медленная версия слова ABS, основанная на применении NEGATE и конструкции IF...THEN, приводится ниже: : ABS DUP 0 < IF NEGATE THEN ; Покажем применение этих операторов на нескольких упражнениях.
Упражнения
1. Определите слово, которое будет печатать наибольшее из трех верхних чисел в стеке. 2. Определите слово, которое будет печатать наименьшее из трех верхних чисел в стеке, оставляя в стеке исходные числа. 3. Определите слово, которое будет выдавать в стек 1, если любое из трех верхних чисел больше 5 (используйте оператор >). 4. Определите слово, которое будет возвращать 1. если все три верхних числа в стеке больше 5 (также используйте один из операторов сравнения), 5. Опишите слово, которое будет возвращать 1, если число на вершине стека больше, чем два следующих, находящихся ниже. 6. Определите слово, которое будет выражать разность температур, два значения которых находятся в стеке, как положительное число, независимо от знака температуры. 7. Определите слово, которое будет находить наибольшую абсолютную величину двух чисел, независимо от того, положительные они или отрицательные. 8. Определите слово, которое будет выдавать абсолютное значение того из двух чисел, которое ближе к нулю, независимо от знака чисел. 9. Используйте слово NEGATE для определения слова, действующего противоположно слову ABS, т.е. возвращающего число, равное по абсолютному значению исходному числу, но всегда отрицательное или нулевое. 10.
Определите слово OTHER-QUAD, которое переводит координаты точки (х.у) в прямоугольной системе координат п противоположный квадрант, сохраняя абсолютное значение х и у. Это значит, что нужно перевести точку из верхнего левого угла в правый нижний, из нижнего левого - в верхний правый и т.д. Например, -23 5 OTHER-QUAD должно в результате выдать 23 -5. 11. Определите слово NEWNEGATE с функцией NEGATE, используя только число и оператор *.
Определение математических функций
Одной из приятных возможностей языка Форт является то, что он позволяет определять математические функции, расширяя язык для математических приложений. В качестве очень простого примера вы уже встречались с определением слова для вычисления квадрата числа: : SQUARE DUP * ; Для-обозначения названий функций удобно, хотя это не общепринято, пользоваться алгебраической записью, заменяя пробелы точками. Например, определение слова для вычисления выражение а^2+b^2 может быть : А2.+.В2 DUP * SWAP DUP * + ; или для выражения а (а + b) : А(А.+.В) (b a - aa+ab) DUP ROT + * ; Для расчета значения сложных выражений лучше всего разбить их на максимально возможное число простых выражений. При этом будет проще следить за состоянием стека, давая определения отдельным словам, и вообще, определения слов должны быть как можно короче и быстрее. Предположим, например, что b лежит в стеке вторым, число а находится на вершине стека и вы хотите найти значение выражения a^2+ab. Можно определить слово: : А2.+.АВ DUP DUP * ROT * + ; которое сначала вычисляет значение а^2, потом ab и затем складывает оба произведения. С другой стороны, выражение может быть разложено на множители а(а + Ь), а это выражение, как мы только что видели, может быть вычислено с помощью : А(А.+.B) DUP ROT + * ; - Очевидно, что второй вариант проще, короче и более быстродействующий.
Упражнения
1. Объем пирамиды вычисляется по формуле Ah/2, где А - площадь основания пирамиды, h -высота пирамиды. Определите слово PYRVOL (объем пирамиды) для вычисления значения функции с округлением результата до ближайшего целого числа. 2.
Определите слово F-> С для пересчета градусов Фаренгейта в градусы Цельсия по формуле С = 5(F - 32)/9. Значения входных и выходных величин должны быть округлены до целых значений. Можете ли вы предложить способ получения значений с точностью до десятых долей градуса? Для этого применяется так называемое масштабирование, о котором мы вскоре расскажем- 3. Напишите слова для вычисления следующих функций, применяя, где возможно, разложение на множители. Дайте вашим словам подходящие названия. (а) а/с + Ь/с (б) а/с + b/с2 (в)a^2+2ab+b^2 (г) За^2+6аb+Зb^2 (д) a^4+4a^3b+6a^2b^2+4ab^3+b^4
Почему используются целые числа?
Из того, что вы уже знаете, ясно, что Форт сталкивается с определенными проблемами при работе с очень большими числами и числами с плавающей запятой (такими, например, как 23.497 или -0.96). Некоторые считают, что использования 16-разрядных и 32-разрядных целых чисел достаточно практически для всех применений микро- и миниЭВМ и работа с числами с плавающей запятой приводит к расточению времени и памяти ЭВМ. Другие, включая авторов, кто использует ЭВМ для научных и технических задач, считают, что, хотя в большинстве применений можно обойтись целыми числами,- числа с плавающей запятой очень нужны для практического применения языка Форт в некоторых классах задач. Несмотря на отсутствие требований в стандартах Форт-79 и Форт-83, в некоторых версиях языка поддерживается в той или иной мере арифметика с числами с плавающей запятой. Мы рассмотрим более детально математические основы применения чисел с плавающей запятой в конце этой главы. Но для этого, а также для того, чтобы понять,как извлечь максимум возможностей из использования целых чисел, мы должны сначала рассмотреть, что представляют собой числа с плавающей запятой.
Замечания о числах с плавающей запятой
Рассмотрим число 1298. Оно может быть представлено следующим образом: 1000 + 200 +90+8.
Число 1298,325 можно представить так : 1000 + 200 + 90 + 8 + 0.3 + 0.02 + 0.005.
Если в числе имеется десятичная запятая, это означает, что часть его, которая стоит после десятичной запятой, представляет сумму дробей, каждая из которых может быть выражена в виде отношения, которое в десятичной системе имеет в знаменателе 10, 100 и т.д.
Таким образом можно представить 0.325 как 3/10 +2/100+5/1000 или, приведя к общему знаменателю, как 300/1000 + 20/1000 + 5/1000 = 325/1000. Таким образом, любое число с плавающей запятой может быть представлено как целое число плюс отношение двух других целых чисел. Так как числа двойной длины используют 32 разряда, то наибольшее представимое целое число равно 4271406735, а с помощью целых чисел могут быть представлены числа с плавающей запятой не меньше этого числа с погрешностью, не превышающей 1/1000000000. Например, число 4271406735.123456789 можно представить как 4271406735 +123456789/1000000000. Это совсем немалый диапазон и малая погрешность, если бы только имелась возможность следить за значением целой и дробной части. Форт оставляет решать эту задачу программисту. С другой стороны, числа с плавающей запятой называются так потому, что правила арифметики и программы для калькуляторов или компьютеров отслеживают положение десятичной запятой, позволяя ей "плавать", где нужно, т.е., положение плавающей запятой в числе автоматически отслеживается машиной.
Число 1 1/2 может быть представлено точно как 1.5. С другой стороны, рассмотрим число 1 1/3, оно не может быть представлено как 1.3, 1.333 и т.д. в десятичной системе, сколько бы ни было разрядов (хотя, если работать в троичной системе счисления, то 1 1/3 можно точно представить числом 1.1). Другими словами, 1 1/3 - это точное'число, в то время как представление его десятичной дробью является приближенным. Приближение может быть достаточно хорошим, но не абсолютно точным. Если числа известны точно и если они могут быть представлены как целое число плюс отношение -двух целых чисел, то лучше всего было бы все числа представлять через целые. Но рассмотрим реальный мир. Точным микрометром можно измерить толщину с погрешностью в лучшем случае 1/1000 см. Для инженера почти все измерения дают. приближенный результат. Большинство констант, которые применяются в науке и технике, нельзя выразить через целые числа или отношением целых чисел.
Например, нельзя точно представить число Пи. Таким образом, во многих областях точное представление целых чисел и отношений не имеет практического смысла. То же справедливо для представления чисел с плавающей запятой. Имеется и еще одна практическая проблема. В технике часто необходимо использовать числа, которые больше или меньше, чем представимые 16- или 32-разрядными числами. Например, в больших ЭВМ применяются 64-разрядные числа. Очень большие и очень малые числа можно представить числами с плавающей запятой с указанием порядка (иногда их называют действительными числами с показательной или "научной" формой записи), используя не больше 32 разрядов. Они выражаются как действительное число с плавающей запятой плюс число 10, возведенное в какую-либо степень. Рассмотрим табл. 4.1.
Степень Число Степень Число 1 (т.е. 10^1) 10 -1 (т.е. 1/10 2 100 -2 1/100 3 1000 -3 1/1000 4 1000 -4 1/10000 Таблица 4.1. Степени числа 10
Мы видели, что 0.395 может быть выражено как 395/1000, поэтому 0.395 = 395/1000 = 395 х 10^ 3. Аналогично мы можем выразить 3950 как 3950 = 0.395 х 104.
Так как многие принтеры не могут напечатать 10^-3 или 10^4, как показано в тексте, в компьютерной нотации опускается число 10 и указывается только показатель степени, перед которым стоит буква Е (от exponent - показатель степени), т.е. 0.395 будет представлено как 3.95Е-3, а 3950 - как 0.395Е4.
Чтобы преобразовать число из показательной формы представления в обычную форму с плавающей запятой, нужно перенести десятичную запятую влево на значение показателя степени, если он положительный (добавляя, если нужно, нули), или вправо, если показатель степени отрицательный. Теперь мы можем выразить очень большие и очень малые числа. Имея только 32 разряда, можно отображать числа больше 1Е-38, т.е. 100000000000000000000000000000000000000, и меньше 1Е-37, или 0.00000000000000000000000000000000000001.
Число, которое предшествует числу 10 в степени, может быть 6-ти разрядным (десятичным). Таким образом, диапазон представления чисел с плавающей запятой 1Е-38 999999Е38.
Необходимо ввести здесь некоторую терминологию. Целое число, предшествующее числу 10 в степени, называется мантиссой (если вы знакомы с логарифмами, то, наверное, знаете почему; если же не знаете, то это не так уж важно), а показатель степени называется порядком.
Арифметические операции с числами с плавающей запятой в показательной форме
Арифметические действия с числами, представленными в показательной форме, выполняются по простым правилам. Вам совершенно не нужно их понимать, для того чтобы понять все за и против математики целых чисел и чисел с плавающей запятой, но правила простые, и сейчас,самое время , их изучить.
Чтобы сложить два числа или вычесть одно число из другого, нужно привести их к одному порядку, а затем сложить или вычесть их мантиссы. Например, 5Е-3 - 2E-2 = 5E-3 - 20Е-3 = -15Е-3= -1.5Е-2 или 12Е10 + 21Е11= 12Е10 +210Е10 = 222Е10 =22,2Е11 Чтобы перемножить два числа, нужно перемножить их мантиссы и сложить порядки. Например, 5Е-3 * 2Е-2 = 10Е-5 = 1Е-4 или 12Е-10 * 21Е11 = 252Е21 = 2.52Е23.
Чтобы разделить два числа, нужно поделить мантиссу делимого на мантиссу делителя и вычесть порядок делителя из порядка делимого. Например, 5Е-3/2Е-2 = 2.5Е-1 или 12Е10/25Е11 = 0.48Е-1 = 0.048.
Представление чисел в форме с плавающей запятой и порядком упрощает арифметические операции с большими и малыми числами.
Точность и погрешность
Нам осталось рассмотреть еще несколько вопросов, прежде чем вернуться к аргументам за и против использования чисел с плавающей запятой или целых чисел. Точность числа с плавающей за пятой выражается числом знаков в записи мантиссы, или, говоря проще, числом десятичных разрядов числа. Так, число 1.23Е5 имеет точность 3 знака, 112.35Е23 - 5 знаков, а 1230 - 4. Но точность и погрешность - это не одно и то же. Если вы пользуетесь измерителем, снабженным нониусом, то вы сможете считать размер с точностью до 1/10 миллиметра. С другой стороны, если вы измеряете размер горошины, то оценить его с точностью до 0.1 миллиметра вы не сможете, так как горошина не идеально круглая.
Вы можете измерить диаметр 4. 5 мм, выражая такой записью, что точность его 2 знака. Вы можете поддаться искушению и добавить еще два нуля, написав 4.500, т.е. с точностью 4 знака. Но погрешность измерения, вероятно, будет ближе к миллиметру или 1 знаку.
Если вы записываете число как 4.5 или 4.500, вы в лучшем случае обманываете себя или того, кто просматривает ваши данные, в худшем случае подтасовываете погрешность измерения. Самый простой способ подорвать доверие к лабораторному отчету о физических или химических измерениях - это указать большое число знаков погрешности, путая ее с точностью. В чем здесь суть? В том, что в реальном мире точность числа лучше 4-6 знаков требуется крайне редко. Одна из причин, почему числа с плавающей запятой так нравятся ученым и инженерам, состоит в том, что из них ясно видна точность числа по количеству приведенных в записи знаков. Запись 1.23Е5 говорит о том, что, хотя измеренное значение приблизительно равно 123000, его точность +-1000. С другой стороны, если число представляется как целое 123000, то большинство представителей технических наук будут считать, что оно" известно с точностью б знаков. Числа с плавающей запятой и указанием порядка лучше всего удовлетворяют потребности науки и техники.
Существует одна важная сфера, где требуется гораздо большая точность. Это денежные расчеты. Бухгалтер никогда не округлит миллион долларов до трех или четырех знаков. В расчетах должны указываться все доллары и центы. И редко кому потребуется величина порядка числа 10 или 11 даже для выражения бюджета крупных государств (хотя иногда крупные компании испытывают трудности в программировании расчетов, так как им приходится оперировать значениями больше триллиона долларов'). Точно так же редко потребуется и порядок меньше -3 (это соответствовало бы десятым долям цента или пенса или одной тысячной иены). Поэтому для бухгалтерских расчетов 32- разрядные числа с плавающей запятой обычно не.подходят. Они не обеспечивают требуемой точности и в то же время имеют избыточный диапазон представляемых чисел.
Программы бухгалтерских расчетов лучше всего составлять с применением целых чисел, но предусматривая две позиции для отображения сотых долей.
Числа с плавающей запятой: за и против
Многие преимущества чисел с плавающей запятой вам уже должны быть понятны. Основные из них: 1) при вычислениях с числами с плавающей запятой обеспечивается отслеживание положения десятичной запятой и величины числа практически без участия программиста; 2) представление чисел с плавающей запятой с указанием порядка позволяет работать как с очень большими, так и очень малыми числами; 3) арифметика с плавающей запятой позволяет задавать точность или погрешность с достаточным количеством знаков; 4) представление чисел с плавающей запятой распространено в мире инженеров и ученых, не склонных доверять представлению результатов измерений в реальном мире с помощью целых чисел. Хотя последняя причина в первую очередь психологическая, она имеет значение для принятия языка большинством практиков.
Многие (но не все) преимущества целочисленной арифметики вам также должны быть понятны: 1) целые числа обеспечивают большую точность при заданном объеме памяти, для 16-разрядных чисел диапазон -32768 - 32767 или - 2147483648 до 2147483647 для 32-разрядных; 2) с помощью целых чисел можно представить как собственно целые, так и действительные числа, имеющие целую и дробную части, например 12.55 = 12 + 55/100; 3) с помощью целых 32-разрядных чисел представляются целые числа и дроби в диапазоне от 1Е-10 и более чем 1Е+10, при этом с точностью на 4 знака больше, чем с помощью 32-разрядных чисел с плавающей запятой.
Но наиболее часто выдвигаемый аргумент в пользу целых чисел - это более высокая скорости работы. Арифметические действия с плавающей запятой производятся компьютером по приведенным здесь правилам. Из них вытекает, что при арифметических действиях с плавающей запятой компьютер должен выполнить большее количество операций сложения, вычитания, умножения или деления, следовательно, для этого требуется в несколько раз больше времени.
Поскольку арифметические действия часто выполняются в циклах много раз, скорость исполнения выдвигается на первое место. Самый существенный аргумент против включения чисел с плавающей запятой в стандарт состоит в том, что в таком случае приносится в жертву скорость работы. Но насколько серьезен этот аргумент? Посмотрим, как работает Фортран или Бейсик (и многие другие языки). Именно с учетом всех за и против, которые мы обсуждаем, другие языки дают программисту свободу принимать решение, обращаться ли с числами как с целыми или как с числами с плавающей запятой. В Форте это также возможно, правда, не во всех версиях.
Возможность пользоваться числами с плавающей запятой должна быть решающим критерием при выборе подходящей версии Форта. И решение этого вопроса возлагается на программиста.
Для некоторых микрокомпьютеров вопрос об использовании чисел с плавающей запятой становится еще более критичным. Как мы увидим дальше, в некоторых компьютерах доступно так называемое сопроцессорное арифметическое устройство, которое практически представляет собой еще один компьютер, предназначенный для выполнения операций над числами с плавающей запятой, причем с очень высоким быстродействием, повышенной точностью и в расширенном диапазоне чисел. Наиболее известным примером может служить сопроцессор фирмы Intel типа 8087, который может быть использован в IBM PC и совместимых с ней ЭВМ. Микросхема 8087 представляет числа 80-разрядами, при этом мантисса числа может иметь до 18 знаков, а порядок от -4600 до 4600 (в буквальном смысле можно представлять числа больше, чем оценка числа электронов во вселенной). По причине того, что 8087 использует стек так же, как Форт, ее стек можно частично использовать для операций с плавающей запятой. Поэтому Форт с сопроцессором 8087 может выполнять некоторые операции с плавающей запятой с потрясающими точностью и диапазоном и значительно быстрее, чем ЦПУ с 16-разрядными числами. Учитывая, что ЭВМ с сопроцессором 8087 могут также работать с памятью объемом до 1 Мбайта, аргументы в пользу применения целых чисел для экономии времени и памяти, а также получения большей точности становятся слишком слабыми.
Имеется надежда, что в будущем появятся версии языка Форт, в стандарт которых будут включены операторы для работы с числами с плавающей запятой. Ну а как быть, если ваша версия Форта не может работать в арифметике с плавающей запятой или вы не можете пожертвовать скоростью работы? Как можно обращаться с числами, которые большинство из нас записывают как действительные числа (с десятичной запятой)? Как вы будете обращаться с дробями? Решать эти вопросы вам поможет масштабирование.
Масштабирование чисел
Слово "масштабирование" применяется в том же смысле, что и в технике, когда делается чертеж, который отображает объект в увеличенном виде. Предположим, что вам нужно рассмотреть синьку часового винта. Если винт имеет длину всего 1 мм, его нужно изобразить в каком-то масштабе, например 100:1. Это значит, что чертеж должен быть выполнен в масштабе, в 100 раз превышающем истинные размеры. Подобным образом карта может быть масштабирована в обратную сторону, скажем, 1 см вместо 1 км, т.е. 1:100000.
Проще всего показать масштабирование на примере. Предположим, вы хотите сложить в столбик доходы, выраженные в долларах, хотя они сейчас выражены в долларах и центах. В целочисленной арифметике на Форте вы, к примеру, не можете сложить 22.98 и 35.53. Но вы можете произвести сложение, если выразите доходы в центах, а не в долларах. Другими словами, вы можете изменить масштаб входных данных и внутреннего представления операций с денежными единицами в 100 раз. В таком случае вы введете 2298 3533 + . и увидите в результате 5831, что, как вы помните, представляет собой 58 долларов 31 цент. Однако в таком виде результат выглядит не очень красиво. Вы можете выразить результат в долларах и центах с помощью следующего слова: $CENTS 100 /MOD U. " Долларов и" . ." центов" ;
Слово $CENTS производит деление числа на 100, округляет результат в сторону нижней границы и печатает его как число долларов. Остаток от деления выражает центы. (В гл. 5 мы узнаем метод, который называется выводом по шаблону, с помощью которого десятичная запятая может быть помещена в числе в любом месте.) Очень удобно для использования при масштабировании чисел слово /МОD) (его иногда называют масштабным оператором), фактически оно позволяет вам "пересчитать" число.
Число на входе выглядит не очень изящно, поскольку в него нельзя еще ввести десятичную запятую. Как мы вскоре увидим, при вводе числа двойной длины необходимо указывать положение десятичной точки, на основании этого Форт распознает числа двойной длины. Но при расчетах Форт игнорирует положение десятичной точки. Как вы видели на примере $CENTS, программист не всегда должен заботиться о масштабировании, эта работа может быть оставлена компьютеру. И не обязательно, чтобы масштабный коэффициент был кратен 10. Вот, например, слово, которое берет из стека число дюймов и переводит их в футы и дюймы: : .FTTN (дюймы -->) 12 /MOD . ." футов и" . ." дюймов" ; Очевидно, можно сделать преобразование в обратную сторону. Определим слово : TOINCHES (футы дюймы - дюймы) SWAP 12 * + ;
Если ввести 10 6 TOINCHES, то мы увидим в стеке длину в дюймах 126.
Умножение на дроби
Предположим, что вы хотите умножить число на 3/4 с помощью калькулятора. Вы можете в действительности умножить на 0.75, зная, что 0.75 равно 3/4. Так как вы не сможете умножить на 0.75 с помощью целых чисел, вы можете на Форте вычислить дробное выражение, умножая на 3/4. Однако позвольте, просто так нельзя разделить 3 на 4, а потом умножить на результат, так как 3 4 / дает 0. Поэтому нужно сначала умножить число на 3, а затем произвести деление, т.е. вам нужно применить оператор */ (он, как и /MOD, называется масштабным оператором). Таким образом, выражение 3 4 / 100 * дает неверный результат 0, в то время как 100 34*/ дает правильный результат 75. Напомним, что во избежание переполнения */ сохраняет промежуточный результат умножения в виде 32-разрядного числа.
Отсюда вытекает общее правило: чтобы обеспечить надлежащую точность смешанной операции, всегда выполняйте операции, которые дают в результате большое число, прежде чем делать деление, поскольку промежуточный результат получается без переполнения. Это еще одна причина необходимости разложения выражения на множители перед его вычислением.
Например, выражение а/х + b/x + с/ х может дать менее точный результат, чем выражение (а+b+с)/х. Чтобы убедиться в справедливости этого утверждения, проверьте это с какими-либо числами.
Оператор */ очень удобен для умножения на дробь с постоянным знаменателем. Например, вы можете определить слово PERCENT (процент) : : PERCENT 100 */ ; так, что выражение 130 50 PERCENT положит в стек число 65 (50% от 130). В некоторых случаях вы хотели бы иметь результат смешанной операции */ с большей точностью, чем получается с по мощью обычного умножения с последующим делением.Наилучший метод избежать потери точности - это убедиться, что перед операцией */ входные операнды уже увеличены с помощью масштабирования для получения желаемой точности. Например, если вы вычисляете 3/4 от 123, то в плавающей арифметике вы получите 92.25, а в целочисленной - 92. Следует изменить масштаб чисел, чтобы операция и */ производилась не с числом 123, а с числом 12300. Это еще один пример того, что, применяя целочисленную арифметику, программист должен заранее все продумать. Но есть еще один способ не потерять точность, если вы по какой-либо причине не хотите предварительным масштабированием увеличить числа: применить оператор */MOD (подобно */, он называется масштабным оператором). Например, выражение 123 3 4 /MOD выдает на вершину стека частное 92 и вторым сверху - остаток 1. Вы можете использовать остаток, если не хотите потерять точность, применяя выражение 123 3 4 */MOD SWAP 100 4 */ которое положит в стек 92 25. Это значит, что на вершине стека находится число, которое представляет собой два разряда после десятичной запятой в представлении с плавающей запятой. Если вы продумаете тщательно программу, то сможете с помощью целых чисел выразить эквивалент числа с плавающей запятой, а, пользуясь форматным выводом, как описано в гл.5, вы сможете даже напечатать результат с десятичной запятой (например, 92.25).
Приближенное представление чисел с помощью рациональных дробей
Предположим, что вы хотите найти длину периметра круга, которая равна диаметру, умноженному на число Пи.
Как можно выразить иррациональное число Пи, т.е. число, которое не может быть выражено в виде отношения двух целых чисел? Для приближенного вычисления можно использовать : PI* 31416 10000 "/ , Если диаметр равен 10 см, то выражение 10 PI* дает 31 - величину периметра с погрешностью 1 см. Очевидно, вам может потребоваться большая точность, для чего вы можете изменить масштаб диаметра на входе, например задать его в миллиметрах. Выражение 100 PI* даст величину 314,т.е. длину периметра 314 мм. С помощью следующего слова вы можете найти площадь круга (которая равна квадрату радиуса, помноженному на Пи или четвертой части квадрата диаметра, умноженной на Пи) : : AREA DUP 4 */ PI* ; Что делает PI* ? Оно просто умножает число на отношение 31416/10000, которое равно 3.1415. 31416/10000 представляет собой апроксимацию иррационального числа Пи рациональной дробью, правда, не очень хорошей апроксимацией, потому что для невысокой точности всего нескольких знаков используются большие числа. Дробно-рациональная аппроксимация применяется для большого разнообразия иррациональных чисел и физических констант, включая гораздо лучшую апроксимацию числа Пи. В табл. 4.2 приведено несколько примеров. Заметим, что все эти апроксимации дают большую точность, чем можно получить, применяя числа одинарной длины.
Константа Отношение Ошибка Пи 355/113 8.5Е-8 2 19601/13860 1.5Е-9 3 18817/10864 1.1Е-9 е 28667/10564 5.5Е-9 с(скорость света) 24559/8192 1.6Е-9
Таблица 4.2. Дробно-рациональная аппроксимация некоторых общеупотребительных констант.
Округление
Если вы делите 26 на 5, то, применяя числа с плавающей запятой, вы получите 5.2, а при делении 29 на 5 - 5.8. Однако при целочисленном делении в обоих случаях мы получим 5. Другими словами, целочисленное деление производит округление с уменьшением (усечение). Если вы будете складывать результаты деления нескольких чисел, то ошибка округления будет накапливаться и результат будет занижен. Можно при округлении следовать школьному правилу: если на первом месте после запятой находится цифра 5 или больше, то округление производится в большую сторону, если меньше 5- то в меньшую.
Программа для этого выглядит не очень сложно. Мы приводим программу деления с округлением, которая делает все, что нужно: : R/ SWAP OVER /MOD SWAP 2 * ROT / + ; Испытайте ее: 26 5 R/ даст в результате 5, а 29 5 R/ даст в результате 6. Вот что здесь происходит. Делитель был помещен на дно стека, затем скопирован на вершину, чтобы осуществить деление с остатком. Остаток был скопирован, а затем поделен на исходный делитель. Это второе частное должно быть равно 1, если первое частное должно быть округлено, причем в этом случае оно должно быть сложено с первым частным. Иначе второе частное должно быть равно 0 и первое частное должно быть оставлено без изменения. Почему эта программа работает? В терминологии деления с плавающей запятой число следует округлить, если дробная часть частного больше или равна 0.5, т.е. дробная часть должна быть умножена на 2, если она больше или равна 1. В целой форме дробная часть удваивается, конечно, удвоенный остаток помещается поверх делителя. Но бывают случаи, когда вы должны сделать округление после деления; это случается, когда вы хотите, чтобы сумма последовательности частных имела ошибку в сторону увеличения или когда вы скорее склонны переоценить результат, чем недооценить его. Чтобы произвести округление, вы просто добавляете 1 к нормальному частному от деления двух целых чисел, если остаток не равен 0. Вот слово.которое это делает на Форт-83: : RUP/ /MOD SWAP 0= 0= + : Слово 0= будет более подробно рассмотрено чуть позже, вкратце оно возвращает 1, если на вершине стека 0, или 0 - в противном случае. Зная это, вы должны понимать, как работает RUP/.
Упражнения
1. Сделайте следующие упражнения в уме: (а) 5Е5х5Е10 (б) 5Е5х5Е-10 (в) 5Е5х5Е0 (г) 5Е-1х5Е-5 (д) 5Е5/5Е10 (е) 5Е5/5Е-5 (ж) 5Е2 + 2Е3 (э) 5Е2 + 5Е-3 (и) 5Е2 - 2Е-3 (к) 5Е2 - 5Е-3 2. Показательная форма записи может применяться и для целых чисел, но не часто. Мантисса должна быть сохранена второй в стеке, а показатель степени - на вершине стека. Таким образом, для 5Е10 в стеке находятся числа 5 10.
Напишите слово ЕХР*, которое должно перемножать два числа, представленных в стеке описанным способом. Например, если в стеке находятся числа 5 10 6 15, то после умножения в стеке будут находиться числа 30 25. Попробуйте это слово на примерах упражнения 1(а-г). 3. Используя идеи упражнения 2, определите слово ЕХР/для деления чисел, представленных в показательной форме записи. Это упражнение не такое простое, как вам кажется, поэтому загляните в ответ в приложении Д, даже если вы чувствуете, что сделали правильно. 4. Определите слово ТОМ (в_метры), которое складывает длину, выраженную в километрах, с длиной, выраженной в метрах. Слово предполагает, что в стеке содержится величина в километрах и величина в метрах в указанном порядке. Результат, выраженный в миллиметрах, должен оставаться в стеке. 5. Определите слово ТОСМММ (в_см_и_мм), которое берет с вершины стека число, представляющее миллиметры, и преобразует его в сантиметры, располагая результат вторым сверху, поверх которого должно располагаться число миллиметров, т.е. число 12.345 должно быть преобразовано в 1234 и 5. 6. Определите слово ТОКМ (в_км), которое выполняет преобразование обратное тому, что делает слово ТОМ из упражнения 4, т.е. берет значение в миллиметрах, а возвращает в стек значение в километрах и метрах. 7. Определите слово ТОРТ(в_футы) по аналогии с упражнением 4, но для преобразования числа миль и футов в футы. В одной законодательной миле содержится 5280 футов. 8.Определите слово ТОМILS(в_мили) для преобразования футов в мили и футы. метры, принимая коэффициент преобразования 1 фут - 0,305 м. 10. Пользуясь словами и идеями упражнений 6-9, напишите программу TOMETRIC (неметрические) для преобразования миль и футов в километры и сантиметры, используя расположение данных такое же, как в предыдущих упражнениях. 11. Пересчет градусов Цельсия в градусы Фаренгейта производится по формуле F = 32 + 9С/5, Определите слово С->Р для преобразования градусов Цельсия в градусы Фаренгейта, при этом температура по Фаренгейту должна выражаться с погрешностью 0.1 градуса.
Позаботьтесь о сохранении погрешности. 12. Напишите слово для определения 1/10 периметра окружности, диаметр которой равен точно 1/2 см. Результат должен быть получен с погрешностью 1 нанометр (1 нм - 1/10000 см). Используйте дробно-рациональную апроксимацию. 13. Определите слово IN->FT для пересчета дюймов в футы с округлением в сторону ближайшего фута, т.е. 13 дюймов нужно округлить до 1 фута, 20 дюймов - до 2 футов.
Числа двойной длины
Мы уже неоднократно упоминали о числах двойной длины. К примеру, при использовании операции */ промежуточный результат запоминается как число двойной длины. Числа двойной длины записываются в два раза большим числом разрядов, чем числа одинарной длины, т.е. для хранения одного числа в стеке используется 32 бита, или 4 байта памяти. Они используются так же, как и числа одинарной длины, за исключением того, что для арифметических операций с ними применяются другие, хотя и похожие слова. Диапазон представления чисел двойной длины со знаком составляет от -2 147 483 648 до 2 147 483 647, диапазон чисел без знака от 0 до 4 294 967 295. Иногда числа двойной длины называют числами двойной точности или еще 32-разрядные числа в стандарте Форта называют двойными числами. Нам кажется, что термин "числа двойной точности" должен распространяться только на числа с плавающей запятой. 32-разрядные целые числа имеют в два раза большую длину, а величина их, конечно, больше не в два раза, что не определяет их точность. Фактически число "двойной длины" означает только то, что число в двоичной форме занимает в два раза больше разрядов, чем число одинарной длины.
Числа двойной длины представляют собой расширение стандарта языка Форт и других версий, т.е. они не присущи стандартному Форту после его загрузки в компьютер. Расширения языка должны быть загружены самостоятельно. В версии MMSFORTH представление числа двойной длины производится словом DBL-LEN# (двойная^длина). Для других версий Форта нужно обратиться к руководству, чтобы узнать, как это делается, или убедиться, что уже предусмотрено.
Если при загрузке Форта это не производится, то нужно осуществить загрузку, после этого мы попробуем сделать несколько примеров. Для того чтобы их понять, вам нужно знать, что слово D. (произносится как дэ-точка) печатает число двойной длины, оно является эквивалентом слова, (точка). Попробуем ввести 1.23 D. и мы увидим на экране число 123. А теперь введите 123. D. и вы увидите тот же самый результат: 123 Ok Теперь введите 1234567890. D. и будет выведено 1234567690 ok
Но если ввести 1234567890 (без десятичной точки в конце),то произойдет переполнение, потому что число было чересчур велико. Не слишком ли это смущает вас? Для чего в числе была нужна точка? И почему число 1234567890. проходит, а число 1234567890 - нет? Ответ простой. Десятичная точка сообщает Форту, что число нужно рассматривать как число двойной длины, т. е- оно должно быть записано в 32 разряда, или 4 байта. При этом совершенно безразлично, где находится десятичная точка, поскольку она в дальнейшем не используется (вы помните, что числа двойной длины используются в целочисленной арифметике). (Примечание: десятичная точка игнорируется не всегда. В MMSFORTH и других версиях Положение десятичной точки запоминается для того, чтобы произвести масштабирование чисел, как мы вскоре увидим.) И поэтому если десятичная точка отсутствует, то она и не обнаруживается при печати числа. Теперь вам стало понятно, что происходит в наших примерах. Очевидно, что число 1234567890 приводит к ошибке, так как вы не сообщили Форту, что это число двойной длины, а для числа одинарной длины оно слишком велико.
Попробуем еще несколько примеров. Убедитесь, что стек пуст и после этого введите 1.23 . . Вы увидите 0 123 ok В этом случае вы вывели два числа одинарной длины, на вершине стека было число 0 и следующее число-123. Попробуйте ввести 65535. U. U. тогда вы получите 0 65535 ok но если ввести 65536. U. U. то вы увидите 1 0 ok
Вы понимаете, в чем тут дело? Если нет, то пропечатайте результаты последних двух примеров в двоичной форме (2 BASE !).
Тогда вы получите такой результат: 0 1111111111111111 для числа 65535 и 1 0 для числа 65536. Если вы переходите от 65535 к 65536, происходит превышение максимального значения числа одинарной длины (целое число находится на вершине стека), при этом младший бит второго числа в стеке устанавливается в "1". То, что происходит с двумя верхними ячейками стека, аналогично тому, что происходит с двумя байтами в стеке, когда число одинарной длины изменяется с 255 на 256. Другими словами, с числами двойной длины Форт обращается так же, как с числами одинарной длины, но для них используется 32 бита. И при этом можно отображать числа двойной длины как со знаком, так и без знака. (Если это вас смущает, просмотрите материал о хранении чисел в двоичной форме из гл. 3.)
Для чисел двойной длины применяется набор арифметических операций, полностью аналогичный набору для чисел одинарной длины, поэтому мы даже не станем приводить примеры. Если вы не совсем понимаете, что делает тот или иной оператор, посмотрите их определения в приложении А. Вот эти операторы: D+, D-, DMAX, DMIN, DABS и DNEGATE (а в Форт-83 еще и D2/). Имеется также набор операторов для сравнения чисел двойной длины, но они будут рассмотрены в гл. 7. В MMSFORTH и других версиях имеется еще ряд дополнительных арифметических операторов, например D*, D/, D*/, D*/MOD и D/MOD. Они действуют так же, как их эквиваленты для чисел одинарной длины, при этом те слова, которые обеспечивают сохранение промежуточных результатов операций с числами одинарной длины в виде 32-разрядных чисел, в данном случае сохраняют промежуточный результат в виде 64-разрядных чисел (т.е. чисел четырехкратной длины).
В MMSFORTH есть два полезных слова #РТ и HI#. Слово #РТ запоминает положение десятичной точки (считая справа налево) в последнем введенном числе двойной длины. Так, например, 12.345 #PT. выдает число 4, в то время как 1.2345 #РТ. выдает число 5. Вы понимаете, что это слово может пригодиться для масштабирования чисел. Если нет, то из упражнений вам станет ясно, как его использовать.
Слово # РТ полезно также для форматного представления чисел, с которым мы познакомимся в гл. 5.
Слово #НI производит удивительное действие. В MMSFORTH все числа воспринимаются как числа двойной длины, но если в числе нет десятичной точки, то в стеке запоминаются только 16 младших битов числа. Независимо от наличия десятичной точки старшие 16 битов числа запоминаются в слове НI#. Таким образом, если ввести 12345678 HI# D. вы увидите число 12345678. Как работает это слово? Для чисел двойной длины старшие 16 бит запоминаются на вершине стека, слово НI# снимает старшие 16 битов и помещает их в стек. Следовательно, если с клавиатуры вводится число двойной длины, а после него слово Н1#, то число всегда запоминается в виде числа двойной длины.
Для манипуляций в стеке с числами двойной длины имеется набор слов, аналогичных словам для работы с числами одинарной длины: 2DROP, 2DUP, 2OVER, 2ROT и 2SWAP. Для доступа к числам двойной длины в памяти имеются также два слова 2! и 2@. Но эти слова так похожи на соответствующие слова для работы с числами одинарной длины, что лучше всего их рассмотреть в упражнениях.
Упражнения
1. Проверьте, что наибольшее 32-разрядное число без знака равно 4294967295. (Подсказка: вспомните о степенях числа 2.) 2. Рассмотрите табл. 2.2 и постройте аналогичную таблицу для манипуляций в стеке с числами двойной длины. 3. Напишите определение слова 2DROP под именем NEW2DROP. 4. Одинаковы ли по своему действию слова 2DUP и DUP DUP? Если да, то почему? и почему, если нет? 5. Определите слово 2DUP под именем NEW2DUP. 6. Определите слова 2SWAP79 и 2SWAP83 через ROLL. 7. Определите слова 2ROT79 и 2ROT83, используя слово ROLL. 8. Определите слова 20VER83 и 20VER79 через слово PICK. Слова, которые мы приводим в упражнениях 3-9, обычно для достижения быстродействия определены на языке ассемблера. 9. Определите слова 2ROLL83 и 2ROLL79, которые должны действовать аналогично ROLL. 10. Определите слова 2PICK83 и 2PICK79, действующие аналогично слову PICK, для тех, кто пользуется версией MMSFORTH. 11.
Определите слово S-> D для преобразования числа одинарной длины в число двойной длины. {Совет: проанализируйте приведенный пример слова Н1# из MMSFORTH.) 12. Что будет находиться в Н1#, если вы введете число 123? 13. Вот слово, которое возводит число 10 в степень п, если п находится на вершине стека: только ту часть числа двойной длины, которая стоит перед десятичной точкой. И еще напишите слово, которое должно возвращать часть числа, находящуюся после десятичной точки. (Указание: 123.25 - 123 + 25/100.)
Смешанные действия с числами одинарной и двойной длины.
В обоих стандартах (Форт-79 и Форт-83) имеются два обязательных слова для смешанных действий, в которых используются числа одинарной и двойной длины. В расширенных версиях Форта их еще больше.
В Форт-79 есть слова U* и U/MOD (аналогами их в Форт-83 являются UM* и UM/MOD). Вы уже раньше узнали, как напечатать число без знака, и, вероятно, вы понимаете, как складывать и вычитать целые числа со знаком и без знака: никакой разницы, нет, с какими числами вы имеете дело. При умножении имеют значение знаки чисел, кроме того, при умножении больших 16-разрядных чисел может возникнуть переполнение. Слово U* (или UM*) производит умножение двух чисел без знака, возвращая в стек число двойной длины. Попробуйте ввести 1000 1000 U* D. и вы увидите 1000000 ok. Очевидно, что результат представлен 32-разрядным числом. Теперь попробуйте ввести 5 -5 U* D. тогда вы увидите 327655
Число -5 было воспринято как 65531, поэтому полученное произведение является верным для данного числа.
Второй оператор для смешанных действии U/MOD (или UM/MOD) производит деление числа двойной длины (находящегося в стеке вторым) на число одинарной длины, помещая в стек остаток , и частное в виде чисел одинарной длины. Можете проверить это на ваших собственных примерах. Кстати сказать, слова U* и UM/MOD (соответственно UM* и UM/MOD) являются частью основного языка, а не расширения его для чисел двойной длины. Расширяющие слова для смешанных oпераций хорошо иллюстрируют слова MMSFORTH.
В табл. 4. 3 показаны их функции. Из этой таблицы ясно видно, что они делают, а также приведены аналоги для чисел одинарной длины.
Слово Действие М* n n --- d М*/ d n n - d (промежуточ. результат - число тройной длины) M+ d n --- d M- d n --- d M/ d n -- n M/MOD d n -- n n DU* ud ud -- uq DU/MOD uq ud--- ud ud Таблица 4.3. Операторы для смешанных действий Обозначения: n - число одинарной длины; d - число двойной длины; q - число учетверенной длины, т.е. 64-разрядное; u - без знака.
Упражнения
1. Используйте UM* для преобразования числа одинарной длины в число двойной длины. Имеет ли значение знак( можно сделать это еще быстрее, попросту помещая 0 в стек.) 2. Вспомните, как хранятся в стеке числа двойной длины в старших и младших ячейках. Имея это в виду, определите следующие смешанные операторы, которые имеются в MMSFORTH: М*, М+, М/ и M/MOD. 3. В чем различие между U/MOD и M/MOD? а также между U* и М* ? 4. Число двойной длины 123.45 помещено в стек и представляет доллары и центы. Определите слово ->DOLLARS, которое должно возвращать число долларов в виде числа одинарной длины. Определите другое слово ->CENTS, которое будет возвращать число центов в виде числа одинарной длины. Проделайте это упражнение, пользуясь словами из MMSFORTH и стандартными операторами. 5. Определите слово FRAC, которое должно умножать число двойной длины на отношение двух чисел одинарной длины, т.е. выражение 500. 3 5, после которого стоит это слово, должно давать в стеке значение 300 в виде числа двойной длины. Теперь определите это слово, пользуясь смешанными операторами MMSFORTH (последнее определение является тривиальным).
Расширение операций над числами с плавающей запятой
Вследствие приведенных раньше в этой главе соображений во многих реализациях Форт включены расширенные возможности для работы с числами с плавающей запятой. Поскольку они не регламентированы стандартом на числа с плавающей запятой, то слова для операций с числами с плавающей запятой отличаются от версии к версии.
В MMSFORTH имеется хороший набор расширяющих слов, которые мы используем в качестве примера. Если в вашем распоряжении есть версия, в которой также используются числа с плавающей запятой, то она скорее всего похожа на MMSFORTH и вам, вероятно, будет интересно проследить за этим обзором, привлекая документацию вашей версии. Мы уже рассмотрели, что представляют собой числа с плавающей запятой и числа в показательной форме. После этого вы, возможно, захотите перейти к материалу о реализации операций с плавающей запятой в MMSFORTH. Мы предполагаем, что вы уже поняли представление чисел с плавающей запятой, "научную" форму представления чисел, как записываются числа в показательной форме и арифметические действия с ними. Во многих компьютерах имеется встроенный интерпретатор языка Бейсик, выполненный на основе ПЗУ - постоянной памяти (по английски ROM- Read Only Memory, т.е. только считываемая память). Этот интерпретатор, написанный в машинных кодах, производит множество операций над числами с плавающей запятой. Кроме обычных операций умножения, деления, сложения и вычитания в нем имеются программы вычисления трансцендентных функций (например, тригонометрических и логарифмической), а также большое количество операций с целыми числами. Вследствие того, что MMSFORTH первоначально был разработан для работы с ЭВМ TRS-80, модель 1, совместимой с IBM PC, и, поскольку обе ЭВМ имеют встроенный Бейсик, "зашитый" в ПЗУ, обычные операции над числами с плавающей запятой производятся путем вызова машинных программ из ПЗУ.
Так как обе машины могут работать с числами с плавающей запятой, представляемыми 32 и 64 разрядами, то MMSFORTH может оперировать с числами одинарной и двойной точности. Обратив внимание, что эти числа принципиально отличаются от целых чисел Форта одинарной и двойной длины (в связи с чем лучше говорить о целых числах как о числах двойной длины, а не двойной точности). Числа с плавающей запятой одинарной точности обеспечивают 5 значащих разрядов > диапазон 9.9999Е-38 - 1Е38.
Числа с плавающей запятой двойной точности имеют точность 11 значащих разрядов и диапазон 9.9999999999999999Е-38 - 1Е38.,Правила ввода и вывода чисел при использовании программ с плавающей запятой из ПЗУ точно такие же, как правила Бейсика уровня 1 для TRS-80 или Бейсика фирмы Microsoft, и приведены они в документации микрокомпьютера. MMSFORTH и большинство других версий Форт для IBM PC также поддерживают арифметический сопроцессор серии 8087, который может быть установлен в персональных компьютерах и других совместимых ЭВМ. Сопроцессор типа 8087 вместе с MMSFORTH позволяет работать с числами имеющими 16 значащих разрядов мантиссы и порядок от -4932 до 4932. Кроме того, он обеспечивает вычисление трансцендентных функций и всех функций обычной арифметики с плавающей запятой и ряда других. Однако микросхема 8087 выполняет все расчеты не с помощью процедур на машинном языке, а с помощью аппаратных средств, которые представляют собой часть электронных схем процессора 8087. Аппаратная арифметика микросхемы 8087 является очень быстродействующей, она в 100 раз быстрее, чем эквивалентная математика на машинном языке и чаще всего быстрее, чем арифметика целых чисел (см. гл. 8). Мы опишем два варианта реализации в MMSFORTH арифметических операций с плавающей запятой: в ПЗУ и на базе сопроцессора 8087.
Арифметика с плавающей запятой, реализованная на ПЗУ
Краткие сведения о функции арифметики с плавающей запятой, реализованной в MMSFORTH на базе ПЗУ, приведены в табл. 4.4. В этом разделе мы опишем некоторые характерные черты этой арифметики, а в следующем - ее отличия от реализации на сопроцессоре 8087.
Числа с плавающей запятой представляются в стеке с 32 разрядами при единичной точности и с 64 разрядами при двойной точности. Таким образом, все слова, которые применяются с целыми числами двойной длины, можно использовать с числами с плавающей запятой одинарной точности, включая слова для задания констант и массивов, которые будут рассмотрены в гл. 6. Перед вводимым числом с плавающей запятой должен быть знак %, Ввод производится в свободном формате, поэтому % 1-01Е2, % 10.1 и % .10100Е3 будут в результате помещать в стек одно и то же число , 10.1 в старших 32 битах как число с плавающей запятой.
D% будет помещать в стек число с плавающей запятой двойной точности. Арифметические и другие математические действия производятся так же, как с целыми числами, поэтому мы их обсуждать не будем. Например, %.551Е3 % 10 F+ TAN F. произведет суммирование чисел 55.1 и 10, получая 65.1, а затем выведет значение тангенса этой величины (по умолчанию - в радианах), которая равна -1.19366. Что же касается комплексных чисел, то они заслуживают дальнейшего рассмотрения, но мы отложим этот вопрос до реализации операций с плавающей запятой на базе сопроцессора 8087. Использование слов для работы с числами с плавающей запятой asder понятно из упражнений.
Упражнения
1. Оцените значения следующих выражений, вводя числа с клавиатуры: (а) 5.5 + 1200 (б) ln(23/5) (в) sin^2(55) + cos^2(45) (углы - в градусах) (г) длину стороны квадрата с площадью 10 квадратных дюймов (д) площадь круга с радиусом 3.25 дюйма. 2. Напишите слово, которое будет брать два целых числа из стека, вычислять площадь прямоугольника со сторонами, выражаемыми этими числами, и печатать ее, обеспечивая не менее 10 значащих разрядов. 3. Напишите определение слова FABS с именем NEWFABS, используя возведение в квадрат и квадратный корень. 4. Напишите слово для определения гипотенузы прямоугольного треугольника по теореме Пифагора. Следующее упражнение предназначено для более подготовленных. 5. Напишите слово, которое с помощью цикла DO-LOOP печатало бы таблицу значений синуса и тангенса для углов до 45 градусов с шагом 1 градус. То же самое сделайте с шагом 0.1 градуса.
Таблица 4.4. Набор операций над числами с плавающей запятой, реализованных в MMSFORTH на базе ПЗУ*
% ( -) Предшествует вводу числа с плавающей запятой F#IN ( - t) Запрашивает ввод числа с плавающей запятой с клавиатуры F. ( f -) Печатает число с плавающей запятой F.R ( f ширина поля -) Печатает число с плавающей запятой, выравненное вправо в поле указанной ширины F+ ( f1 f2 -- f3) Возвращает сумму двух чисел F- ( f1 f2 -- f3) Возвращает разность двух чисел f1-f2 F* ( f1 f2 - f3) Умножает два числа f1*f2 F/ ( f1 f2 - f3) Делит два числа (f1/f2) FABS ( f - f1) Берет абсолютную величину числа с плавающей запятой FMINUS ( f -- -f) Изменяет знак числа; эквивалентно NEGATE SGN ( f - n) Возвращает -1, если f0 FCOMP ( f1 f2 - n) Сравнивает два числа с плавающей запятой: n=-1, если f1f2 FOG ( f - log(f)) Возвращает натуральный логарифм числа f FOG10 ( f - lg(f)) Возвращает десятичный логарифм числа f EXP < f - exp(f)) Возвращает значение числа е в степени f 10^ ( f - 10^f) Возводит число 10 в степень f X^Y ( f1 f2 - f1^f2) Возвращает f1, возведенное в степень f2 1/X ( f - 1/f) Возвращает число, обратное f FIX ( f1 - f2) Возвращает число с плавающей запятой, округленное до целого INT ( f1 - f2) Возвращает вместо числа с плавающей запятой ближайшее целое снизу CINT ( f - n) Возвращает 16-разрядное ближайшее целое, меньшее f I-F ( n - f) Преобразует 16-разрядное целое число в число с плавающей запятой SQR ( f1 - f2) Возвращает квадратный корень от f1 RND ( f - f) Возвращает случайное число с плавающей запятой, как на Microsoft Бейсике DEGREES ( -) Дает указания Форту принимать углы в градусах RADIANS ( -) Дает указания Форту принимать углы в радианах SIN ( f1 - f2) Возвращает значение синуса от f1 COS ( f1 - f2) Возвращает значение косинуса от f1 TAN ( f1 - f2) Возвращает значение тангенса от f1 ATN ( f1 - f2) Возвращает значение арктангенса от f1 ATN2 ( f1 f2 - f3) Возвращает значение арктангенса угла отрезка, соединяющего начало координат с точкой х,у PI ( - f) Возвращает константу Пи RAD ( - f) Возвращает константу для перевода градусов в радианы L10 ( - f) Возвращает натуральный логарифм 10 DF#IN ( - df) Запрашивает ввод числа с плавающей запятой двойной точности с клавиатуры DF. ( df -) Печатает число с плавающей запятой двойной точности DF.R ( df ширина поля-) Печатает число с плавающей запятой двойной точности, выравненное в поле указанной длины вправо DF+ ( df1 df2 - df3) Возвращает сумму (df1 + df2) DF- ( df1 df2 - df3) Возвращает разность (df1 - df2) DF* ( dfi df2 - df3) Возвращает произведение (df1*df2) DF/ ( df1 df2 - df3) Возвращает частное (df1/df2) DFABS ( df1 - df2) Возвращает абсолютное значение df1 DFMINUS ( df1 - df2) Изменяет знак df1 DSGN ( df - n) Возвращает -1, если df0 DFCOMP ( df1 df2 - n) Сравнивает два числа с плавающей запятой двойной точности, возвращая -1, если df1df2 FDF ( f - df) Преобразует число с плавающей запятой одинарной точности в число двойной точности с плавающей запятой DFF ( df - f) Преобразует число с плавающей запятой двойной точности в число одинарной точности с плавающей запятой CP+ ( Cp1 Cp2 -Cp3) Возвращает сумму двух комплексных чисел CP- ( Ср1 Cp2 -Cp3) Возвращает разность двух комплексных чисел(cp1 ср2) CP* ( Cp1 Cp2 -Cp3) Возвращает произведение двух комплексных чисел (ср1*ср2) CP/ ( Cp1 Cp2 -Cp3) Возвращает частное от деления двух комплексных чисел (ср1/ср2) MAG ( cp - f) Возвращает модуль комплексного числа PHASE ( cp - f) Возвращает аргумент или фазу комплексного числа CPMINUS ( cp - -cp) Изменяет знак комплексного числа с плавающей запятой CONJG ( cр -- ср) Возвращает сопряженное комплексное число с плавающей запятой R-Р ( ср -- f f) Преобразует прямоугольные координаты в полярные Р-R ( f f- ср) Преобразует полярные координаты в прямоугольные
* Набор программ содержит также несколько слов для работы с числами двойной и учетверенной длины, например 4 SWAP, Обозначения: f - число с плавающей запятой; df - число двойной точности с плавающей запятой; ср - комплексное число с плавающей запятой; n - 16- битовое число со знаком; адр - адрес.
Арифметика с плавающей запятой, реализованная на сопроцессоре 8087
Реализация операций с плавающей запятой на основе микросхемы 8087 отличается от реализации на ПЗУ несколькими существенными моментами: 1) вместо стека в памяти ЭВМ используется стек микросхемы, поэтому версии Форта могут иметь различия; преимущество этого стека - в быстродействии, недостаток - в ограниченной глубине стека; 2) большая часть операций выполняется на аппаратном уровне микросхемы 8087, а не на уровне машинных программ; 3) числа в стеке микросхемы 8087 хранятся как 80-битовые, а в памяти как 64-битовые, что дает дополнительные 16 знаков точности по сравнению с 64-битовыми числами, и 9 разрядов для 32-битовых чисел. Диапазон значений порядка от -4932 до 4932 (80 битов), от -306 до 307 (64 бита) и от -38 до 38 (32 бита); 38 (32 бита); 4) скорость операций с плавающей запятой на микропроцессоре 8087 в 100 раз выше, чем на ПЗУ, и для некоторых операций даже превосходит скорость вычислений с 16-разрядными целыми числами; умножение на микросхеме 8087 производится на 45% быстрее, чем с целыми числами, а сложение на 5% медленнее; 5) гарантируется обычно 16 точных разрядов при фиксированном формате мантиссы. Ввод чисел так же, как и в варианте на ПЗУ, производится в свободном формате, за исключением того, что диапазон вводимых чисел увеличивается- А так как в микросхеме имеется отдельный стек, то можно использовать оба стека одновременно без взаимных помех. Например, можно написать выражение тогда получится следующий результат: 291 0.1000000000000000Е60
Это значит, что вычисления с целыми числами и числами с плавающей запятой производятся одновременно и независимо в двух стеках: складываются числа 55 и 236 и перемножаются 1Е50 и 1Е10 и печатаются оба результата.
Конечно, такая запись может привести к путанице и поэтому не рекомендуется. Скорость работы микросхемы 8087 в режиме плавающей запятой удивительна: она превосходит скорость микрокомпьютеров и приближается к скорости универсальных вычислительных машин. Время вычисления 100.000 операций сложения с плавающей запятой составляет чуть больше 10 с, причем большая часть времени затрачивается на организацию зацикливания программы. Очень мощная универсальная ЭВМ Cyber фирмы CDC на языке Фортран затрачивает на это немного более 1 с, но при этом гарантируется только 12 разрядов точности, в то время как на со процессоре 8087 - 16 знаков точности. Становится реальным вычисление таких выражений, как 1000!, что практически невозможно даже на мини-ЭВМ, Практический пример применения операций в плавающей арифметике из работы авторов: нужно было смоделировать процесс изменения свойств воды в озере под действием естественных процессов за период 1000 лет. Для моделирования необходимо было решать численными методами 15 дифференциальных уравнений на каждые 4 ч моделируемого процесса. На суперЭВМ CDC Cyber в режиме разделения времени решение заняло один день. При этом ЦПУ машины было занято приблизительно около часа, причем стоимость машинного времени составила несколько сотен долларов. Решение этой задачи на языке Бейсик в скомпилированной форме заняло бы более одного месяца работы микроЭВМ IBM PC в монопольном режиме. При использовании MMSFORTH на микросхеме 8087 результат был получен менее чем за один день, фактически быстрее, чем на суперЭВМ. Решение немногих задач, подобных описанной, окупают затраты на микрокомпьютер и программное обеспечение и очень экономно расходуют время большой ЭВМ.
Отсутствующие функции плавающей арифметики, реализованные на ПЗУ: P.R, FABS, FMINUS (вместо нее имеется FNEGATE), FIX, INT, CINT, I-P, RND, RAD и L10. Кроме того, исключены слова для манипуляций в стеке с числами учетверенной и двойной длины, так как в стеке микропроцессора 8087 они не требуются.
Вследствие того, что арифметические действия с плавающей запятой реализованы в микросхеме 8087 аппаратными средствами и отличаются от реализации на ПЗУ, набор программ MMSFORTH содержит некоторые новые функции, а другие были исключены. Однако все отсутствующие функции легко можно определить на основе имеющихся, и мы в этом убедимся в следующих упражнениях.
Таблица 4.5. Дополнительный набор функций с плавающей запятой, реализованных на микросхеме 8087, не обеспечиваемых в реализации на ПЗУ
$F. (8f - адр n) Преобразует число с плавающей запятой в адрес, 87> (8f -- n ) Преобразует число с плавающей запятой в 16-ти битовое число >87 (n - 8f) Преобразует 16-битовое целое в число с плавающей запятой ?DEGREES (- n) Возвращает 1, если значение угла вводится в градусах, 0 - если в радианах COS.SIN (8f -- 8f 8f) Возвращает на вершину стека синус, вторым сверху - косинус CP*R (Cp 8f - Cp) Умножает каждую часть комплексного числа на число с плавающей запятой CPNEGATE (Cp1 - -Cp1) см. CPMINUS в табл. 4,4 D87> (8f - d) Преобразует число с плавающей запятой в 32-битовое целое число D>87 (d - 8f) Преобразует 32-битовое целое в число с плавающей запятой DF87> (8f - df) Преобразует число с плавающей запятой из формата микросхемы 8087 в число двойной точности с плавающей запятой формата ПЗУ DF>87 (d - 8f) Преобразует число двойной точности с плавающей запятой из формата ПЗУ в число с плавающей запятой формата микросхемы 8087 F87> (8f - f) Преобразует число с плавающей запягой из формата микросхемы 8087 в формат ПЗУ F>87 (f - 8f) Преобразует число с плавающей запятой из формата ПЗУ в формат микросхемы 8087 FDROP (8f -) Очищает стек микросхемы 8087 FDUP (8f1 - 8f1 8f1) Эквивалент DUP для микросхемы 8087 FNEGATE (8f1 - 8f2) См. FMINUS в табл. 4.4 FOVER (8f1 8f2 - 8f1 8f2 8f1 ) Эквивалент OVER для 8087 FROT (8f1 8f2 8f3 - 8f2 8f3 8f1) Эквивалент ROT в стеке 8087 FSWAP (8f1 8f2 - 8f2 8f1) Эквивалент SWAP в стеке 8087 FVAL (8f1 - $) Возвращает адрес символьной счетной строки, если задано число в формате 8087 LOG2 (8f1 - 8f2) Возвращает логарифм числа с плавающей запятой по основанию 2 ONE (- 8f) Помещает в стек 8087 число 1.0000000
Примечания: обозначения такие же, как в табл. 4.4; 8f - число с плавающей запятой в формате стека 8087; $ - счетная строка (см. гл. 9).
О мнимых и комплексных числах
MMSFORTH - один из немногих языков программирования для микрокомпьютеров, который может работать с комплексными числами. Если вам нужны для работы комплексные числа, то вы, конечно, представляете, что это такое. Тем не менее мы приводим краткий обзор для интересующихся и тех, кто хотел бы узнать об операциях с комплексными числами, даже не в MMSFORTH. Комплексное число представляет собой сумму действительного и мнимого числа, т.е- а + bi, где а называется действительной частью, b - мнимой частью, a i является корнем квадратным из -1. (Действительное число, как отсюда следует, это "нормальное", не мнимое число).Так как не существует такого числа, квадрат которого является каким-либо отрицательным числом, то i не имеет физического смысла и называется мнимым числом или просто мнимым. Тем не менее мнимые числа порождаются в результате некоторых математических действий. И мнимые, и комплексные числа широко используются учеными и инженерами. Комплексные числа проще всего представить как векторы или, еще проще, как точки с координатами (х,у) в системе координат, называемой комплексной плоскостью, плоскостью Гаусса или круговой диаграммой Аржана. Величина а отображает координату х, b - координату у. Модуль, или величина, числа, - это длина отрезка, проведенного из начала координат в данную точку, в то время как угол между осью х и отрезком называется аргументом или фазой. Сопряженное комплексное число - это число, у которого мнимая часть умножается на -1, т.е. сопряженным к a+bi является число а-bi. Слова MMSFORTH MAG (от magnitude- модуль, величина), PHASE (фаза) и CONJG (сопряженный) делают именно то, что от них можно ожидать по названию (см. табл. 4.4). В MMSFORTH действительная и мнимая части числа помещаются в стек так, что на вершине находится мнимая часть. В реализации на ПЗУ они вводятся с помощью слова СР%, хотя их можно так же просто вводить как два числа с плавающей запятой, как это должно делаться в реализации на сопроцессоре 8087 в MMSFORTH.
В таблице 4. 4 показано, каким образом MMSFORTH позволяет выполнять умножение комплексного числа на действительное число, умножение, деление, сложение и вычитание двух комплексных чисел, определение модуля и фазы, а также нахождение сопряженного комплексного числа. MMSFORTH позволяет также преобразовывать представление точки в системе прямоугольных координат в полярные координаты. Последние можно рассматривать как форму обращения с комплексными числами, которая заменяет число с компонентами а и b, выражая его через модуль и аргумент (фазу). (В прямоугольной системе координат точка представляется величинами х,у; в полярной системе координат - величиной отрезка, проведенного из начала координат в данную точку, и углом между этим отрезком и осью х, т.е. фазой.)
Следует заметить, что выражение "действительные числа" употребляется специалистами по компьютерам некорректно, так как у математиков nmn имеет другой смысл. Многие специалисты по компьютерам считают, что действительные числа - это синоним чисел с плавающей запятой, в отличие от целых чисел. Но это совершенно неправильно: любое число, которое не является мнимым или комплексным, является действительным, будь оно целое или с плавающей запятой. Неверная терминология, возможно, обязана своим происхождением условностям, принятым в Фортране, где числа с плавающей запятой называются "действительными" (real). К сожалению, эта терминология проникла и в описание сопроцессора 8087 и ее следует избегать.
Если у вас есть набор программ MMSFORTH для работы с комплексными числами или какая-либо другая версия, вы сможете проделать следующие упражнения.
Упражнения
1. Следующие выражения могут быть вычислены в реализации функций с плавающей запятой на микросхеме 8087. Перепишите их, чтобы можно было работать в версии MMSFORTH, реализованной на ПЗУ: (а) $ 5.5 5 % 6.5 8 + F+ (б) $ 35 10 I-F F+ SIN (в) % -55 FMINUS (г) $ 35 FDUP 5 COS FSWAP SIN F+ . F. 2. Определите слова Форта для микросхемы 8087, сходные по звучанию и функциям со следующими словами, реализованными на ПЗУ: FABS.
CINT, I-F, RAD, L1О. 3. Определите слово FACT. предназначенное для вычисления факториала в-реализации на микросхеме 8087. Попробуйте вычислить 1000! Зафиксируйте время исполнения операции. (Факториал числа 3 равен 3х2х1, факториал 6 - 5х4х3х2х1.) 4. Определите величину ошибки дробно-рациональной апроксимации числа Пи, приведенной в табл. 4.2. 5. Используя комплексные числа, определите слово ANGLE (угол), для того чтобы найти угол между гипотенузой и прилегающей стороной прямоугольного треугольника, если в стеке находятся значения длин обеих прилегающих сторон треугольника (рис.4.1).
6. Аналогично определите слово HYPOT (гипотенуза) для вычисления длины гипотенузы. 7. По определению, величина рН представляет собой взятый со знаком минус логарифм концентрации ионов водорода: pH=-log[H-]. Определите слово, которое по значению в стеке величины рН и абсолютному значению увеличения концентрации ионов водорода, выраженному в форме с плавающей запятой, рассчитывает фактическое увеличение рН. Можете ли вы предложить способ, как сделать это в целочисленной арифметике? 8. Одна из рутинных задач робототехники - преобразовать перемещение плеча, описываемого координатами х.у, в приращения радиуса и угла. Например, на рис.4.1 для перемещения из точки (х1,у1) в точку (х2,у2) требуется приращение угла (угол1-угол2) и приращение радиуса (радиус 1-радиус2). Определите слово ARMMOVE (движение, плеча), которое по заданным в стеке значениям х1,у1,х2,у2 будет выдавать в стек значения величин (угол1-угол2) и (радиус1-радиус2). За положительное приращение угла принимается движение по часовой стрелке. Для решения вам потребуются тригонометрические функции или комплексные числа.
Выводы
Как и обещали в начале этой главы, мы привели обзор основных понятий, связанных с числами. Некоторые из них, как, например, числа двойной длины, должен изучить каждый программирующий на Форте, в то же время числа с плавающей запятой могут потребоваться только тем, кто будет иметь дело с математическими функциями.
Тем не менее после этого обзора вы должны почувствовать, что Форт имеет мощные средства для решения математических научно-технических задач. Не иронично ли, что многие критики Форта считают его слабостью то, "что он не подходит для таких задач? Основанием для критики является в основном использование* обратной польской записи вместо алгебраической и отсутствие операций с дейcтвительными числами (в том числе с плавающей запятой) в стандартах языка и его простейших реализациях. Но ни одна из этих причин не становится трудно преодолимым препятствием для Форта. Применение стека обеспечивает Форту высокое быстродействие, которое так необходимо для математических приложений, а операции с плавающей запятой имеются в большинстве версий Форта. Форт используется в очень широком диапазоне отраслей науки и техники, например в лазерной энергетике, радиоастрономии, технике охраны окружающей среды, физической океанографии, робототехнике. Многие поняли, что Форт более мощный и простой язык, чем "классический" язык науки и техники Фортран. Мы надеемся, что язык найдет еще большее распространение среди тех, кто использует математику в технических дисциплинах.