12.6 Приведение типов и доступ к компонентам чисел

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

[Функция] float number &optional other

Преобразует любое некомплексное число в число с плавающей точкой. При отсутствии необязательного параметра, если number уже является числом с плавающей точкой, то оно и будет возвращено, иначе число будет преобразовано в single-float. Если аргумент other указан, тогда он должен быть числом с плавающей точкой, и number будет конвертирован в такой же формат как у other.

Смотрите также coerce.


[Функция] rational number
[Функция] rationalize number

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

rational предполагает, что число с плавающей точкой совершенно точно, и возвращает рациональное число математически эквивалентное значению числа с плавающей точкой.

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

Следующие тождества всегда справедливы

(float (rational x) x)  x

и

(float (rationalize x) x)  x

То есть, преобразование числа с плавающей точкой любым из методов туда и обратно даёт исходное число. Различие в том, что rational обычно имеет более простую недорогую реализацию, тогда как rationalize представляет более «красивое» число.


[Функция] numerator rational
[Функция] denominator rational

Эти функции принимают рациональное число (целое или дробное) и возвращают в качестве целого числа числитель или знаменатель дроби, приведённое к каноническому виду. Числитель целого числа и является этим числом. Знаменатель целого числа 1. Следует отметить, что

(gcd (numerator x) (denominator x))  1

Знаменатель будет всегда строго положительным числом. Числитель может быть любым целым числом. Например:

(numerator (/ 8 -6))  -4
(denominator (/ 8 -6))  3


В Common Lisp’е нет функции fix, потому что есть несколько интересных способов преобразовать нецелое число к целому. Эти способы представлены функциями ниже, которые выполняются не только преобразование типа, но также некоторые нетривиальные вычисления.

[Функция] floor number &optional divisor
[Функция] ceiling number &optional divisor
[Функция] truncate number &optional divisor
[Функция] round number &optional divisor

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

floor преобразовывает аргумент путём отсечения к отрицательной бесконечности, то есть, результатом является наибольшее целое число, которое не больше чем аргумент.

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

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

round преобразовывает аргумент путём округление к ближайщему целому. Если исходное число находится посередене (то есть содержит слагаемое + 0.5), тогда оно округляется к ближайщему чётному числу (которое делится на два).

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

Аргументfloorceilingtruncateround
 2.6  2  3  2  3
 2.5  2  3  2  2
 2.4  2  3  2  2
 0.7  0  1  0  1
 0.3  0  1  0  0
-0.3 -1  0  0  0
-0.7 -1  0  0 -1
-2.4 -3 -2 -2 -2
-2.5 -3 -2 -2 -2
-2.6 -3 -2 -2 -3





Если указан второй аргумент divisor, тогда аргумент number будет разделен на divisor, а затем уже будут проведены вышеописанные действия. Например, (floor 5 2)  (values (floor (/ 5 2))), но первый вариант потенциально эффективнее.

This statement is not entirely accurate; one should instead say that (values (floor 5 2))  (values (floor (/ 5 2))), because there is a second value to consider, as discussed below. In other words, the first values returned by the two forms will be the same, but in general the second values will differ. Indeed, we have

(floor 5 2)  2 and 1
(floor (/ 5 2))  2 and 1/2

for this example.

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

In other words, the one-argument case returns an integer and fractional part for the number: (truncate 5.3)  5.0 and 0.3, for example.

Каждая из функций возвращает два значения. Второе значение является остатком и может быть получено с помощью multiple-value-bind или другими подобными конструкциями. Если любая из этих функций получает два аргумента x и y и возвращает q и r, тогда qy + r = x. Первое значение результата q всегда целочисленное. Остаток r целочисленный, если оба аргумента были целочисленными, и рациональное, если оба аргумента были рациональными, и с плавающей точкой, если один из аргументов был с плавающей точкой. Если при вызове был указан один аргумент, то тип данных остатка всегда такой же как и этот аргумент.

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


[Функция] mod number divisor
[Функция] rem number divisor

mod выполняет операцию floor для двух аргументов и возвращает второй результат floor. Таким же образом, rem выполняет операцию truncate для двух аргументов и возвращает второй результат truncate.

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

(mod 13 4)  1 (rem 13 4)  1
(mod -13 4)  3 (rem -13 4)  -1
(mod 13 -4)  -3 (rem 13 -4)  1
(mod -13 -4)  -1 (rem -13 -4)  -1
(mod 13.4 1)  0.4 (rem 13.4 1)  0.4
(mod -13.4 1)  0.6 (rem -13.4 1)  -0.4


[Функция] ffloor number &optional divisor
[Функция] fceiling number &optional divisor
[Функция] ftruncate number &optional divisor
[Функция] fround number &optional divisor

Эти функции похожи на floor, ceiling, truncate и round за исключением того, что результат (первый из двух) всегда целое число, а не число с плавающей точкой. Это примерно, как если бы ffloor передала аргументы в floor, а затем применила к первому результату float и вернула полученную пару значений. Однако, на практике ffloor может быть реализована более эффективно. Такое же описание подходит к остальным трём функциям. Если первый аргумент является числом с плавающей точкой, и второй аргумент не точнее типа первого, тогда первый результат будет такого же типа как первый аргумент. Например:

(ffloor -4.7)  -5.0 and 0.3
(ffloor 3.5d0)  3.0d0 and 0.5d0


[Функция] decode-float float
[Функция] scale-float float integer
[Функция] float-radix float
[Функция] float-sign float1 &optional float2
[Функция] float-digits float
[Функция] float-precision float
[Функция] integer-decode-float float

Функция decode-float принимает числа с плавающей точкой и возвращает три значения.

Первое значение является мантиссой и числом того же типа, что и аргумент. Второе значение является целочисленной экспонентой. Третье значение отображает знак аргумента (-1.0 или 1.0) и является и числом того же типа, что и аргумент . Пусть b есть система счисления для отображения чисел с плавающей точкой, тогда decode-float делит аргумент на b в некоторой степени, чтобы привести значение в промежуток включая 1/b и не включая 1 и возвращает частное в качестве первого значения FIXME. Однако, если аргумент равен нулю результат равен абсолютному значению аргумента (то есть, если существует отрицательный ноль, то для него возвращается положительный ноль).

Второе значение decode-float является целочисленным экспонентой e, которая равняется степени, в которую было возведено b. Если аргумент равен нулю, то может быть возвращено любое целое число, при условии, что тожество, описанное ниже для scale-format, имеет место быть.

Третье значение decode-float является числом с плавающей точкой в том же формате, что и аргумент, абсолютное значение которого равно 1, и знак совпадает со знаком аргумента.

Функция scale-float принимает число с плавающей точкой f (не обязательно между 1/b и 1) и целое число k, и возвращает (* f (expt (float b f ) k)). (Использование scale-float может быть более эффективным, чем использование возведения в степень или умножения и позволяет избежать переполнений).

Следует отметить, что

(multiple-value-bind (signif expon sign)
                     (decode-float f )
  (scale-float signif expon))
 (abs f )

и

(multiple-value-bind (signif expon sign)
                     (decode-float f )
  (* (scale-float signif expon) sign))
 f

Функция float-radix возвращает (в качестве целого числа) основание b для числа с плавающей точкой.

Функция float-sign возвращает такое число с плавающей точкой z, что z и float1 имеют одинаковый знак, и z и float2 имеют равное абсолютное значение. Аргумент float2 по-умолчанию имеет значение (float 1 oat1). Таким образом (float-sign x) всегда возвращает 1.0 или -1.0 в таком же формате и с тем же знаком, что и x. (Следует отметить, что если реализация содержит различные представления для отрицательного и положительного нулей, тогда (float-sign -0.0)  -1.0.)

Функция float-digits возвращает целочисленное количество цифр используемых в представлении аргумента. Функция float-precision возвращает целочисленное количество цифр в мантиссе аргумента. Если аргумент равен нулю, то результатом также будет ноль. Для нормализованных чисел с плавающей точкой, результаты float-digits и float-precision будут такими же. Но в случае с денормализованными числами или нулём точность будет меньше чем количество цифр в представлении.

Функция integer-decode-float похожа на decode-float, но в качестве первого значение возвращает масштабированную целочисленную мантиссу. Для аргумента f , это число будет строго меньше чем

(expt b (float-precision f ))

на не менее чем

(expt b (- (float-precision f ) 1))

за исключением того, что если f равно нулю, тогда целочисленное значение также будет равно нулю.

Второе значение имеет такую же связь с первым значением, как и для decode-float:

(multiple-value-bind (signif expon sign)
                     (integer-decode-float f )
  (scale-float (float signif f ) expon))
 (abs f )

Третье значение integer-decode-float будет 1 или -1. _______________

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

___________________________________________________________________________________________________________

[Функция] complex realpart &optional imagpart

Аргументы должны быть некомплексными числами. Результатом является число, которое имеет действительную часть realpart и мнимую imagpart, возможно конвертированные к одному типу. Если imagpart не указаны, тогда используется (coerce 0 (type-of realpart)). Необходимо отметить, что если обе части комплексного число рациональны, и мнимая часть равна нуля, то результатом будет только действительная часть realpart так как в силу вступят правила канонизации. Таким образом результат complex не всегда является комплексным числом. Он может быть просто рациональным числом (rational).


[Функция] realpart number
[Функция] imagpart number

Эти функции возвращают действительную и мнимую части комплексного числа. Если number не является комплексным числом, тогда realpart возвращает это число, а imagpart возвращает (* 0 number), другими словами, 0 того типа, каким был аргумент.

A clever way to multiply a complex number z by i is to write

(complex (- (imagpart z)) (realpart z))

instead of (* z #c(0 1)). This cleverness is not always gratuitous; it may be of particular importance in the presence of minus zero. For example, if we are using IEEE standard floating-point arithmetic and z = 4 + 0i, the result of the clever expression is − 0 + 4i, a true 90 rotation of z, whereas the result of (* z #c(0 1)) is likely to be

(4 + 0i)(+0 + i) = ((4)(+0) − (+0)(1)) + ((4)(1) + (+0)(+0))i
= ((+0) − (+0)) + ((4) + (+0))i = +0 + 4i

which could land on the wrong side of a branch cut, for example.