2.1 Числа

В Common Lisp’е определены некоторые виды чисел. Они подразделяются на целочисленные (integer); дробные (ratio); с плавающей точкой (floating-point) с четырьмя видами представления, действительные (real) и комплексные (complex).

Числовой (number) тип данных захватывает все числовые типы. Для удобства также предоставлены имена для некоторых числовых подтипов. Целые числа и дроби принадлежат к рациональному (rational). Рациональные числа и с плавающей точкой — к действительному (real). Действительные (real) и комплексные (complex) — к числовому (number) типу.

Несмотря на то, что эти типы выбирались из математической терминологии, соответствие не всегда полное. Модель целочисленных (integers) и дробных (ratios) типов полностью совпадает с математической. Числа с плавающей точкой (float) могут использоваться для аппроксимации действительных (real) чисел: рациональных (rational) и иррациональных (irrational). Действительный (real) тип включает все Common Lisp числа, которые отображают действительные (real) математические числа, однако для математических иррациональных (irrational) аналогии в Cоmmon Lisp’е нет. Только действительные (real) числа могут быть отсортированы с помощью функций <, >, <= и >=. (Ох жеш FIXME).

2.1.1 Целые числа

Целочисленный тип данных предназначен для отображения математических целых чисел. В отличие от большинства языков программирования, Common Lisp принципиально не навязывает ограничений на величину целого числа. Место для хранения больших чисел выделяется автоматически по мере необходимости.

В каждой реализации Common Lisp’а есть интервал целых чисел, которые хранятся более оптимально, чем другие. Каждое такое число называется fixnum, и число не являющееся fixnum’ом называется bignum. Common Lisp спроектирован так, чтобы скрыть различие настолько, насколько это возможно. Различие между fixnums и bignums видимо пользователю, только в тех местах, где важна эффективность работы алгоритма. Какие числа являются fixnums зависит от реализации; обычно это числа в интервале от − 2n to 2n − 1, включительно, для некоторого n не меньше 15. См. most-positive-fixnum и most-negative-fixnum.

fixnum должен быть супертипом для типа (signed-byte 16), и в дополнение к этому, значения array-dimension-limit должны принадлежать fixnum (разработчики должны выбрать интервал fixnum, чтобы в него можно было включить наибольшее число поддерживаемых измерений для массивов). ________________________________________________________________

Обоснование: Эта спецификация позволяет программистам объявлять переменные в переносимом коде типа fixnum для эффективности. Fixnums гарантированно заключают в себе множество знаковых 16-битных чисел чисел (это сравнимо с типом данных short int в языке программирования C). В дополнение к всему, любой корректный индекс массива должен быть fixnum, и в таком случае переменные, которые хранят индексы массива (например переменная в dotimes) могут быть объявлены как fixnum в переносимом коде.

___________________________________________________________________________________________________________

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

0      ;Нуль
-0      ;Это всегда значит то же, что и 0
+6      ;Первое совершенное число
28      ;Второе совершенное число
1024.      ;Два в десятой степени
-1      ;eπi
15511210043330985984000000.      ;факториал от 25 (25!), скорее все bignum

Целые числа могут быть представлены с основаниями отличными от десяти. Синтаксис:

#nnrddddd or #nnRddddd

означает, что целое число с основанием nn определённое с помощью цифр и букв ddddd. Более точное описание: символ #, не пустая последовательность десятичных цифр представляющих десятичное число n, r (или R), опционально знак + или -, и последовательность цифр для заданной системы счисления (система счисления должна быть между 2 и 36, включительно). Для заданной системы счисления могут использоваться для задания числа только корректные символы. Например для восьмеричного числа могут использоваться только цифры от 0 до 7 включительно. Для систем счисления больших десятичной, могут использоваться буквы алфавита в любом регистре в алфавитном порядке. Двоичные, восьмеричные и шестнадцатеричные основания можно использовать с помощью следующих аббревиатур: #b для #2r, #o для #8r, #x для #16r. Например:

#2r11010101      ;Другой способ определения числа 213
#b11010101      ;то же самое
#b+11010101      ;то же самое
#o235      ;То же самое, в восьмеричной системе
#xD5      ;То же самое, в шестнадцатеричной системе
#16r+D5      ;То же самое
#o-300      ;Десятичное число -192, записанное восьмеричным числом
#3r-21010      ;То же самое, в троичное системе счисления
#25R-7H      ;То же самое с основанием 25
#xACCEDED      ;181202413, в шестнадцатеричной системе

2.1.2 Дробные числа

Дробное число — это число отображающее математическое отношение между двумя целыми числами. Целые и дробные числа вместе составляют тип рациональных (rational) чисел. Образцовое отображение дробных чисел - это целое число, если значение целое, в противном случае это отношение двух целых чисел, числителя и знаменателя, наибольший общий делитель которых единица, в котором знаменатель положителен (и фактически больший чем единица, иначе дробь является целым числом). Дробь записывается с помощью разделителя /, так: 3/5. Есть возможность использовать нестандартную запись такую, как 4/6, но Lisp функция prin1 всегда выводит дробь в стандартной форме.

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

Дробные числа могут быть записаны так: опционально знак + или -, за ним следуют две не пустые последовательности цифр разделённых с помощью / . Такой синтаксис может быть описан так:

ratio ::= [sign] {digit}+ / {digit}+

Вторая последовательность не может состоять только из нулей. Например:

2/3                 ;Это каноническая запись
4/6                 ;Это неканоническая запись предыдущего числа
-17/23              ;Не очень интересная дробь
-30517578125/32768  ;Это (−5∕12)15
10/5                ;Это каноническая запись для 2

Для задания дробей в системе счисления отличной от десятичной, необходимо использовать спецификатор основания (один из #nnR, #O, #B или #X) как и для целых чисел. Например:

#o-101/75         ;Восьмеричная запись для -65/61
#3r120/21         ;Третичная запись для 15/7
#Xbc/ad           ;Шестнадцатеричная запись для 188/173
#xFADED/FACADE    ;Шестнадцатеричная запись для 1027565/16435934

2.1.3 Числа с плавающей точкой

Common Lisp позволяет реализации содержать один и более типов чисел с плавающей точкой, которые все вместе составляют тип float. Число с плавающей точкой является (математически) рациональным числом формы sfbe−p, где s + 1 или − 1, является знаком; b целое число большее 1, является основанием для представления; p положительное целое, является точностью (количество цифр по основанию b) числа с плавающей точкой; f положительное целое между bp−1 и bp − 1 (включительно), является мантиссой; и e целое число, является экспонентой. Значение p и интервал e зависит от реализации, также может быть «минус ноль». Если «минус ноль» отсутствует, тогда 0.0 и -0.0 оба интерпретируются, как ноль с плавающей точкой. ___________________________________________________________________

Заметка для реализации: The form of the above description should not be construed to require the internal representation to be in sign-magnitude form. Two’s-complement and other representations are also acceptable. Note that the radix of the internal representation may be other than 2, as on the IBM 360 and 370, which use radix 16; see float-radix.

___________________________________________________________________________________________________________

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

Определение точности для этих категорий зависит от реализации. Однако, примерная цель такая, что короткий тип с плавающий точкой должен содержать точность как минимум 4 позиции после запятой (и также должен иметь эффективное представление в памяти); одинарный тип с плавающей точкой — как минимум 7 знаков после запятой; двойной тип с плавающей точкой — как минимум 14 знаков после запятой. Предполагается, что размер точности (измеряется в битах и рассчитывается как p log 2b) и экспоненты (измеряется в битах и рассчитывается как логарифм с основанием 2 от (1 плюс максимальное значение экспоненты) должен быть как минимум таким же большим как значения из таблицы 2.1.


Таблица 2.1: Рекомендуемый размер для точности и экспоненты для типа с плавающей точкой
Формат Минимальная точность Минимальный размер экспоненты
Короткое 13 бит 5 бит
Одинарное24 бит 8 бит
Двойное 50 бит 8 бит
Длинное 50 бит 8 бит

Числа с плавающей точкой записываются в двух формах десятичной дробью и компьютеризированной научной записью: необязательный знак, затем не пустая последовательность цифр с встроенной точкой, затем необязательная часть определения экспоненты. Если определения экспоненты нет, тогда требуется точка, и после неё должны быть цифры. Определение экспоненты составляется из маркера экспоненты, необязательного знака и негустой последовательности цифр. Для ясности приведена БНФ для записи чисел с плавающей точкой.

число-с-плавающей-точкой ::= [знак] {цифра}* точка {цифра}+ [экспонента]
|  [знак] {цифра}+ [точка {цифра}*] экспонента
знак ::= + | -
точка ::= .
цифра ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
экспонента ::= маркер-экспоненты [знак] {цифра}+
маркер-экспоненты ::= e | s | f | d | l | E | S | F | D | L

Если определение экспоненты отсутствует или если используется маркер экспоненты e (или E), тогда используемые формат точности не задан. Когда такое представление считывается и конвертируется во внутренний формат объекта числа с плавающей точкой, формат задаётся с помощью переменной *read-default-float-format*; первоначальное значение данной переменной single-float.

Буквы s, f, d и l (или их эквиваленты в верхнем регистре) явно задают использование типа: короткий, одинарный, двойной и длинный, соответственно.

Примеры чисел с плавающей точкой:

0.0                         ;Ноль с плавающей точкой в формате по умолчанию
0E0                         ;Также ноль с плавающей точкой в формате по умолчанию
-.0                         ;Это может быть нулём или минус нулём
                            ; в зависимости от реализации
0.                          ;Целый ноль, не с плавающей точкой!
0.0s0                       ;Ноль с плавающей точкой в коротком формате
0s0                         ;Также ноль с плавающей точкой в коротком формате
3.1415926535897932384d0     ;Аппроксимация числе пи в двойном формате
6.02E+23                    ;Число Авогадро в формате по умолчанию
602E+21                     ;Также число Авогадро в формате по умолчанию
3.010299957f-1              ; log 102, в одинарном формате
-0.000000001s9              ;eπi в коротком формате

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

Тогда как Common Lisp содержит терминологию и систему обозначений для включения 4 различных типов чисел с плавающей точкой, не все реализации будут иметь желание поддержки такого большого количества типов. Реализации разрешается предоставлять меньшее, чем 4, количество внутренних форматов чисел с плавающей точкой, в таком случае как минимум один из этих типов будет «общим» для более одного внешнего имени короткого, одинарного, двойного и длинного в соответствии со следующими правилами:

_________________________________________________________________________

Заметка для реализации: Рекомендуется предоставлять столько различных типов чисел с плавающей точкой, сколько возможно, используя при этом таблицу 2.1 в качестве указаний. В идеальной ситуации, короткий формат числа с плавающей точкой должен быть «быстрым», в частности, не требующим выделения места в куче. одинарный должен приближаться к стандарту IEEE для одинарного формата. двойной должен приближаться к стандарту IEEE для двойного формата. [231716].

_________________________________________________________________________________________________________________

2.1.4 Комплексные числа

Комплексные числа (тип complex) представляются в алгебраической форме, с действительной и мнимой частями, каждая из которых является не комплексным числом (целым, дробным, или с плавающей точкой). Следует отметить, что части комплексного числа не обязательно числа с плавающей точкой; в это Common Lisp похож на PL/I и отличается от Fortran’а. Однако обе части должны быть одного типа: обе рациональные, или обе какого-либо формата с плавающей точкой.

Комплексные числа могут быть обозначены с помощью записи символа #C с последующим списком действительной и мнимой частей. Если две части, как было отмечено, не принадлежат одному типу, тогда они будут преобразованы в соответствие с правилами преобразования чисел с плавающей точкой описанными в главе 12.

#C(3.0s1 2.0s-1)     ;Действительная и мнимая части в коротком формате
#C(5 -3)             ;Целое Гаусса
#C(5/3 7.0)          ;Будет преобразовано в #C(1.66666 7.0)
#C(0 1)              ;Мнимая единица, i

Тип заданного комплексного числа определяется с помощью списка: слова complex и типа компонентов; например, специализированное представление для комплексных чисел с частями принадлежащими типу «короткое с плавающей точкой», будет выглядеть так (complex short-float). Тип complex включает все представления комплексных типов.

Комплексное число типа (complex rational), в котором части принадлежат рациональному типу, никогда не может содержать нулевую мнимую часть. Если в результате вычислений получится комплексное число с нулевой мнимой частью, то данное число будет автоматически конвертировано в не комплексное дробное число, равное действительной часть исходного числа. Это называется правилом канонизации комплексного числа. Данное правило не применяется для комплексных чисел с плавающими точками, то есть #C(5.0 0.0) и 5.0 различные числа.