3.2 Основы чисел с плавающей запятой

Числа с плавающей запятой полезны для представления чисел, которые не являются целыми. Диапазон чисел с плавающей запятой такой же, как и диапазон типа данных C double на используемой вами машине. На почти всех компьютерах, поддерживаемых Emacs, это формат плавающей запятой IEEE IEEE binary64, который стандартизирован в IEEE Std 754-2019 и более подробно обсуждается в статье Дэвида Голдберга “Что должен знать каждый ученый-компьютерщик о арифметике с плавающей запятой”. На современных платформах операции с плавающей запятой в целом соответствуют стандарту IEEE-754; однако результаты не всегда округляются правильно на некоторых системах, в частности, на 32-битных x86.

На некоторых старых компьютерных системах Emacs может не использовать плавающую запятую IEEE. Мы знаем об одной такой системе, на которой Emacs работает корректно, но не соответствует IEEE-754: это VAX, работающий под NetBSD с GCC 10.4.0, где используется формат VAX ‘D_Floating’. Главные компьютеры, производные от IBM System/370, и их компилятор XL/C также способны использовать формат чисел с плавающей запятой в шестнадцатеричном представлении, но Emacs еще не был собран в такой конфигурации.

Синтаксис чтения чисел с плавающей запятой требует наличия либо десятичной точки, либо экспоненты, либо обоих. Необязательные знаки (‘+’ или ‘-’) предшествуют числу и его экспоненте. Например, ‘1500.0’, ‘+15e2’, ‘15.0e+2’, ‘+1500000e-3’ и ‘.15e4’ — это пять способов записи числа с плавающей запятой, значение которого равно 1500. Все они эквивалентны. Как и Common Lisp, Emacs Lisp требует как минимум одной цифры после десятичной точки в числе с плавающей запятой, у которого нет экспоненты; ‘1500.’ является целым числом, а не числом с плавающей запятой.

Emacs Lisp рассматривает -0.0 как численно равный обычному нулю при числовых сравнениях, таких как =. Это соответствует стандарту плавающей запятой IEEE, который утверждает, что -0.0 и 0.0 численно равны, даже если другие операции могут их различать.

Стандарт плавающей запятой IEEE поддерживает положительную и отрицательную бесконечность в качестве значений с плавающей запятой. Он также предусматривает класс значений, называемых NaN, или “не число”; числовые функции возвращают такие значения в тех случаях, когда нет правильного ответа. Например, (/ 0.0 0.0) возвращает NaN. NaN никогда не равен никакому значению численно, даже самому себе. NaN несут знак и значащую часть, а нечисловые функции считают два NaN равными, когда их знаки и значащие части совпадают. Значащие части NaN зависят от машины, как и цифры в их строковом представлении.

При работе с NaN и знаками нуля нечисловые функции, такие как eql, equal, sxhash-eql, sxhash-equal и gethash, определяют, являются ли значения неотличимыми, а не численно равными. Например, когда x и y являются одним и тем же NaN, (equal x y) возвращает t, тогда как (= x y) использует численное сравнение и возвращает nil; наоборот, (equal 0.0 -0.0) возвращает nil, тогда как (= 0.0 -0.0) возвращает t.

Вот синтаксисы чтения для этих специальных значений с плавающей запятой:

infinity

‘1.0e+INF’ и ‘-1.0e+INF’

not-a-number

‘0.0e+NaN’ и ‘-0.0e+NaN’

Бесконечности и NaN недоступны на устаревших системах, в которых отсутствует арифметическая плавающая запятая IEEE. Например, на VAX образца 1980 года Lisp читает ‘1.0e+INF’ как большое, но конечное число с плавающей запятой, а ‘0.0e+NaN’ как другой нечисловой объект Lisp, который вызывает ошибку при численном использовании.

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

Функция: isnan x

Этот предикат возвращает t, если его аргумент с плавающей запятой является NaN, иначе nil.

Функция: frexp x

Эта функция возвращает ячейку cons (s . e), где s и e — это соответственно значащая часть и экспонента числа с плавающей запятой x.

Если x конечно, то s — это число с плавающей запятой в диапазоне от 0.5 (включительно) до 1.0 (исключительно), e — это целое число, и x = s * 2**e. Если x равно нулю или бесконечности, то s равно x. Если x является NaN, то s также является NaN. Если x равно нулю, то e равно 0.

Функция: ldexp s e

При заданном численном значащем значении s и целом показателе e эта функция возвращает число с плавающей запятой s * 2**e.

Функция: copysign x1 x2

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

Функция: logb x

Эта функция возвращает двоичную экспоненту x. Точнее, если x конечно и не равно нулю, значением является логарифм основанный в 2 от |x|, округленный вниз до целого числа. Если x равно нулю или бесконечности, значением является бесконечность; если x является NaN, значением является NaN.

(logb 10)
     ⇒ 3
(logb 10.0e20)
     ⇒ 69
(logb 0)
     ⇒ -1.0e+INF