Типы данных для хранения вещественных чисел в PostgreSQL

от автора

В статье рассматриваются особенности типов данных для хранения вещественных чисел в PostgreSQL.

Типы данных PostgreSQL для работы с вещественными числами:

1) float4, синоним real, синоним float(1..24)

2) float8, синоним float, синоним double precision, синоним float(25..53)

3) numeric, синоним decimal. Диапазон для этого типа значительный: 131072 цифр до точки и 16383 цифр после точки. Но если при определении типа указать numeric(точность, масштаб), то максимальные значения точности и масштаба 1000. numeric можно объявить с отрицательным масштабом: значения могут округляться десятков, сотен, тысяч.

Кроме чисел и null поддерживаются значения Infinity, -Infinity, NaN.

Поля типов данных фиксированной длины не могут вытесняться в TOSAT-таблицу, переменной длины (numeric) могут.

float4 обеспечивает точность 6 разрядов (значащих чисел в десятичной системе счисления), float8 обеспечивает точность 15 разрядов. Последний разряд округляется:

select 12345678901234567890123456789.1234567890123456789::float4::numeric;             numeric -------------------------------        12345700000000000000000000000 (1 row) select 12345678901234567890123456789.1234567890123456789::float8::numeric;             numeric -------------------------------        12345678901234600000000000000 (1 row)

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

Недостаток типов float4 и float8 данных в том, что добавление к большому числу маленького числа эквивалентно добавлению нуля:

select (12345678901234567890123456789.1234567890123456789::float8 + 123456789::float8)::numeric;             numeric ------------------------------- 12345678901234600000000000000 (1 row)

В примере добавление значения 123456789::float8 эквивалентнно добавлению нуля.

При добавлении к numeric точность сохраняется и разряды не теряются:

select 1234567890123456789.123456789::numeric + 0.00000000000000000000123456789::numeric as numeric;                       numeric                       ---------------------------------------------------  1234567890123456789.12345678900000000000123456789 (1 row)

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

При округлении float8 учитывается шестнадцатый разряд:

округление float8

округление float8

Параметр конфигурации extra_float_digits

Параметром extra_float_digits можно уменьшить число цифр в текстовом представлении чисел float8, float4 и геометрических типов. Диапазон значений от -15 до 3 включительно

значения 1,2,3 эквивалентны. Параметр влияет только на отображение, на вычисления и приведения к типу numeric не влияет

Влияет на текстовое представление float8, float4 и геометрических типов. Значение по умолчанию 1. Значения параметра extra_float_digits  1,2,3 эквивалентны:

show extra_float_digits; 1 select 1234567890.123456789::float8, 1.123456789::float4;       float8               |      float4        ---------------------------+--------------------        1234567890.1234567  |      1.1234568 (1 row) set extra_float_digits = 3; select 1234567890.123456789::float8, 1.123456789::float4;       float8               |      float4        ---------------------------+--------------------        1234567890.1234567  |      1.1234568 (1 row) 

Значение ноль и отрицательные значения убирают из вывода разряды с округлением:

set extra_float_digits = 0; select 1234567890.123456789::float8, 1.123456789::float4;       float8               |      float4        ---------------------------+--------------------        1234567890.12346    |      1.12346 (1 row) set extra_float_digits = -1; select 1234567890.123456789::float8, 1.123456789::float4;       float8               |      float4        ---------------------------+--------------------        1234567890.1235     |      1.1235 (1 row) set extra_float_digits = -2; select 1234567890.123456789::float8, 1.123456789::float4;       float8               |      float4        ---------------------------+--------------------        1234567890.123      |      1.123 (1 row) set extra_float_digits = -5; select 1234567890.123456789::float8, 1.123456789::float4;       float8               |      float4        ---------------------------+--------------------        1234567890          |      1 (1 row) 

Параметр конфигурации extra_float_digits влияет только на представление (отображение, вывод). На вычисления и приведения к типу numeric не влияет:

select 1234567890.123456789::float8::numeric, 1.123456789::float4::numeric;       float8               |      float4        ---------------------------+--------------------        1234567890.12346    |               1.12346 (1 row)

Округление может убрать много разрядов:

reset extra_float_digits;  select 234567890.199999989::float8::numeric, 1.19999999123::float4::numeric;       float8               |      float4        ---------------------------+--------------------        234567890.2         |              1.2 (1 row) 

Хранение вещественных чисел

Тип numeric имеет переменную длину и для небольших чисел хранит данные компактнее, чем float8 : точность 15 «десятичных разрядов», цифр в десятичном виде, то есть цифр до и после точки в десятичном виде, то есть если разрядов не хватает, то убираются десятичные и потом целочисленные цифры и заменяются нулями.

Цветом выделены поля трёх типов

Цветом выделены поля трёх типов

Функция проверки размера поля также показывает занимаемое полями место:

select pg_column_size(c1), pg_column_size(c2), pg_column_size(c3) from t5;  pg_column_size | pg_column_size | pg_column_size  ----------------+----------------+----------------               8 |              4 |              5               8 |              4 |             13               8 |              4 |              5               8 |              4 |              7

Все три типа данных поддерживают значения Infinity NaN -Infinity. Пример:

truncate t5; insert into t5 values ('Infinity', 'Infinity', 'Infinity'); insert into t5 values ('NaN', 'NaN', 'NaN'); select * from t5;     c1    |    c2    |    c3     ----------+----------+----------  Infinity | Infinity | Infinity       NaN |      NaN |      NaN (2 rows) select lp_off, lp_len, t_hoff, t_data from heap_page_items(get_raw_page('t5','main',0)) order by lp_off;  lp_off | lp_len | t_hoff |              t_data               --------+--------+--------+----------------------------------    8096 |     39 |     24 | \x000000000000f87f0000c07f0700c0    8136 |     39 |     24 | \x000000000000f07f0000807f0700d0 (2 rows) 

Разрядность результата деления numeric

Разрядность результата деления двух чисел типа numeric:

1) не менее 16 значащих цифр, то есть не хуже, чем float8

2) не меньше, чем разрядность любого из входных параметров.

Для вычисления квадратного корня и других операций с потерей точности действует аналогичное правило. Для операторов сложения, вычитания, умножения и других потери точности нет.

Пример:

insert into t5 values (1,1, 1.000000000000000000000000000000000001/3);  select lp, lp_off, lp_len, t_hoff, t_data from heap_page_items(get_raw_page('t5','main',0))  order by lp desc limit 1;  lp | lp_off | lp_len | t_hoff |                  t_data  ----+--------+--------+--------+-----------------------------------   5 |   7912 |     57 |     24 | \x000000000000f03f0000803f2b7f92050d050d050d050d050d050d050d050d060d  (5 rows) select * from t5 order by ctid desc limit 1;  c1 | c2 |                   c3                    ----+----+---------------------------------------   1 |  1 | 0.333333333333333333333333333333333334 
разрядность деления чисел в PostgteSQL

разрядность деления чисел в PostgteSQL

 Заключение

 Для обработки десятичных чисел можно использовать numeric, а не float4 и float8. Преимущество numeric в том, что обычно, в столбцах хранятся небольшие числа и поля numeric используют меньше места, чем типы фиксированной длины. Точность вычислений при использовании numeric не меньше 16 разрядов, то есть не хуже, чем у float8. Точности float4 (real) может быть недостаточно: всего 6 десятичных разрядов. Все три типа данных поддерживают значения Infinity NaN -Infinity.


ссылка на оригинал статьи https://habr.com/ru/articles/924378/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *