![]() |
|
|||
WebMoney: WMZ Z294115950220 WMR R409981405661 WME E134003968233 |
Visa 4274 3200 2453 6495 |
SQLite хранит целочисленные значения в 64-битном формате
twos-complement¹. Это дает диапазон хранения от -9223372036854775808
до +9223372036854775807, включительно. Целые числа в этом диапазоне точны.
Так называемый "REAL" или значения с плавающей точкой
сохранены в формате
IEEE 754
Binary-64 ¹. Это дает диапазон положительных значений примерно от
1.7976931348623157e+308 до 4.9406564584124654e-324 с эквивалентным диапазоном
отрицательных величин. binary64 может также быть 0.0 (и -0.0), положительной
и отрицательной бесконечностью и "NaN" или "Не-число".
Значения с плавающей точкой приблизительны. Обратите пристальное внимание на последнее
предложение в предыдущем параграфе: Если вам нужен точный ответ, вы не должны использовать значения binary64 с
плавающей запятой в SQLite или ни в каком другом продукте. Это не ограничение
SQLite. Это математическое ограничение, врожденное от дизайна
чисел с плавающей запятой. — SQLite обещает сохранить 15 старших значащих цифр значения с плавающей
точкой. Однако, это не делает гарантий точности вычислений на значениях с
плавающей точкой, поскольку никакие такие гарантии невозможны.
Выполнение математики на значениях
с плавающей запятой вводит ошибку. Например, рассмотрите то, что происходит,
при попытке вычесть два числа с плавающей запятой подобной величины: Результатом, показанным выше (0.0000011126), является правильный ответ.
Но если вы делаете это вычисление, используя binary64 с плавающей запятой,
ответ, который вы получаете, 0.00000095367431640625,
ошибка приблизительно 14%. Если вы делаете много подобных вычислений как
часть вашей программы, ошибки складываются так, что ваш конечный результат
мог бы быть абсолютно бессмысленным. Ошибка возникает, потому что только первые 15 значительных цифр каждого
числа сохранены точно, и первое различие между этими двумя вычитаемыми
числами находится в 16-й цифре. Формат с плавающей запятой binary64 использует 64 бита на число.
Следовательно, есть 1.845e+19 различных возможных значения с плавающей
точкой. С другой стороны, есть бесконечно много вещественных чисел в
диапазоне от 1.7977e+308 до 4.9407e-324. Значит binary64 не может
представлять все возможные вещественные
числа в этом диапазоне. Приближения требуются. Значение IEEE 754 с плавающей запятой является целым числом,
умноженным на тсепень 2: M это мантисса, E экспонента. M и E это integer. Для Binary64 M это 53-bit integer, E это 11-bit integer, которое смещено
так, чтобы представлять диапазон значений
между -1074 и +972, включительно. Обычное описание IEEE 754 более сложно, и важно понять добавленную
сложность, если вы действительно хотите оценить детали,
достоинства и ограничения IEEE 754. Однако, описание целого числа, показанное
здесь, в то время как не совсем точно, легче понять, и этого достаточно в
целях этой статьи. Не каждое десятичное число меньше чем с 16 значительными цифрами может
быть представлено точно как binary64-число. На самом деле большинство
десятичных чисел с цифрами направо от десятичной точки испытывает недостаток
в точном binary64-эквиваленте. Например, если у вас есть колонка базы данных,
которая предназначается, чтобы хранить цену на пункт в долларах и центах,
единственная покупательная сила центов, которая может быть точно
представлена, 0.00, 0.25, 0.50, и 0.75. Любые другие числа направо от
десятичной точки приводят к приближению. Если вы предоставите значение
"price" 47.49, то число будет представлено в binary64 как: Это работает так: Это число очень близко к 47.49, но это не точно.
Это немного слишком большое. Если мы уменьшаем M на один к 6683623321994526,
чтобы у нас было следующее меньшее возможное значение binary64: Это второе число слишком маленькое. Первое число ближе к требуемому
значению 47.49. Но это не точно. Большинство десятичных значений работают в
IEEE 754 примерно так. Помните ключевое мнение, которое мы высказали выше:
Если вы не помните ничто иное о значениях
с плавающей запятой, пожалуйста, не забывайте эту ключевую идею. Точность, обеспеченная IEEE 754 Binary64, достаточна для большинства
вычислений. Например, если "47.49" представляет цену, и инфляция достигает
2% в год, то цена повышается приблизительно на 0.0000000301
в секунду. Ошибка в зарегистрированном значении
47.49 представляет ценность приблизительно 66 наносекунд инфляции.
Таким образом, если 47.49 точна при вводе,
эффекты инфляции заставят истинное значение точно равняться стоимости
(47.4900000000000019895196601282805204391479492187)
с поправкой меньше, чем в одну десятимиллионную секунды.
Конечно, тот уровень точности достаточен в большинстве ситуаций. Расширение ieee754 преобразовывает число с плавающей точкой между
представлением binary64 и форматом M×2E.
Другими словами, в выражении: расширение ieee754 преобразовывает между F и (M, E) и назад. Расширение ieee754 не часть объединения,
но это включено по умолчанию в CLI.
Если вы захотите включить расширение ieee754 в свое приложение,
необходимо будет собрать и загрузить его отдельно.
Функция ieee754(F) SQL берет единственный аргумент с плавающей запятой в
качестве своего входа и возвращает последовательность, которая
похожа на это: За исключением того, что M и E заменяются мантиссой и экспонентой числа с
плавающей точкой. Например: С другой стороны, версия с 2 аргументами ieee754()
берет M и E, оценивает и преобразовывает их в соответствующее значение F: Текстовый вывод формы с одним аргументом ieee754()
больше для удобства чтения, но это неудобно использовать в качестве части
большего выражения. Следовательно, ieee754_mantissa() и ieee754_exponent()
были добавлены, чтобы возвратить M и E, соответствующие их значениям
отдельного аргумента F. Например: SQL-функция ieee754_to_blob(F) преобразовывает число с плавающей
точкой F в 8-байтовый BLOB, который является обратным порядком байтов
binary64-кодирования того числа. Функция ieee754_from_blob(B)
идет другим путем, преобразовывая 8-байтовый blob в значение с плавающей
запятой, которое представляет кодирование binary64. Так, например, если вы читаете
Wikipedia, что кодирование для минимального положительного значения
binary64 это 0x0000000000000001, тогда можно найти соответствующее
значение с плавающей точкой: Или пойдите другим путем: Расширение decimal обеспечивает десятичную систему исчисления произвольной
точности на числах, сохраненных как текстовые строки.
Поскольку числа сохранены к произвольной точности и как текст, никакие
приближения не необходимы. Вычисления могут быть сделаны точно. Десятичное расширение (в настоящее время) не часть
объединения SQLite. Однако, это включено в
CLI. Есть три доступные математических функции: Первые три функции соответственно добавляют, вычитают и умножают свои
аргументы и возвращают новую текстовую строку, которая является десятичным
представлением результата. Аргумент интерпретируется как текст.
Нет никакого оператора деления, потому что десятичное деление часто не
производит конечный десятичный результат. Функция decimal_pow2(N) вернет 2.0 в степени N, где N это
integer от -20000 до +20000. Используйте decimal_cmp(A,B), чтобы сравнить два десятичных значения.
Результат будет отрицательным, нолем или положительным, если A будет меньше,
равно или больше B, соответственно. Функция decimal_sum(X) это совокупность, как встроенная функция
sum(),
за исключением того, что decimal_sum()
вычисляет свой результат с произвольной точностью и поэтому точна. Десятичное расширение обеспечивает последовательность сопоставления
"decimal", которая сравнивает десятичные текстовые
строки в числовом порядке. decimal(X) и decimal_exp(X) производят десятичное представление для
входа X. decimal_exp(X) возвращает результат в экспоненциальном
представлении (с "e+NN" в конце) и decimal(X) вернет чистое десятичное число
(без "e+NN"). Если вход X является значением с плавающей точкой, он расширен
до его точного десятичного эквивалента. Например:
Choose any three.
1. Как SQLite хранит числа
Значения с плавающей точкой приблизительны.
¹
Исключение: Расширение R-Tree
хранит информацию как 32-битные значения с плавающей точкой
или целочисленные значения.1.1.
Точность с плавающей запятой
1152693165.1106291898 -1152693165.1106280772 0.0000011126
1.2.
Числа с плавающей точкой
M × 2E
1.2.1.
Непредставляемые числа
6683623321994527 × 2-47
47.49000000000000198951966012828052043914794921875
47.4899999999999948840923025272786617279052734375
Значения с плавающей точкой приблизительны.
1.2.2. Это достаточно близко?
2. Расширения для контакта с числами с плавающей точкой
2.1. Расширение ieee754.c
F = M × 2E
2.1.1. Функция ieee754()
'ieee754(M,E)'
sqlite> .mode box
sqlite> SELECT ieee754(47.49) AS x;
+-------------------------------+
| x |
+-------------------------------+
| ieee754(6683623321994527,-47)|
+-------------------------------+
sqlite> select ieee754(6683623321994527,-47) as x;
+-------+
| x |
+-------+
| 47.49 |
+-------+
2.1.2.
Функции ieee754_mantissa() и ieee754_exponent()
sqlite> .mode box
sqlite> SELECT ieee754_mantissa(47.49) AS M, ieee754_exponent(47.49) AS E;
+------------------+-----+
| M | E |
+------------------+-----+
| 6683623321994527 | -47 |
+------------------+-----+
2.1.3.
Функции ieee754_from_blob() и ieee754_to_blob()
sqlite> .mode box
sqlite> SELECT ieee754_from_blob(x'0000000000000001') AS F;
+-----------------------+
| F |
+-----------------------+
| 4.94065645841247e-324 |
+-----------------------+
sqlite> .mode box
sqlite> SELECT quote(ieee754_to_blob(4.94065645841247e-324)) AS binary64;
+--------------------+
| binary64 |
+--------------------+
|X'0000000000000001' |
+--------------------+
2.2.
Расширение decimal.c
sqlite> .mode qbox
sqlite> select decimal(47.49);
+------------------------------------------------------+
| decimal(47.49) |
+------------------------------------------------------+
| '47.49000000000000198951966012828052043914794921875'|
+------------------------------------------------------+