Общая рекомендация
Аргументы всех математических функций могут задаваться в виде < SQL-параметра >
, который должен содержать спецификацию типа данных параметра, например,
select abs(? (double)); -67.41 | 67.41| select mod(? (double), ? (int)); 236.78 23 | 6.78000000000003|
СУБД ЛИНТЕР поддерживает два типа приближенных вещественных данных: REAL
и DOUBLE
(DOUBLE PRECISION
), различающихся между собой точностью представления данных (тип данных REAL
имеет меньшую точность по сравнению с DOUBLE
). При математических вычислениях с использованием одновременно разных вещественных типов данных точность промежуточных (или окончательных) результатов может снижаться. Поэтому рекомендуется при использовании функций от аргументов типа DOUBLE PRECISION хранить значения аргументов этих функций также в столбцах типа DOUBLE PRECISION
, а не REAL
.
Пример
Сравнение точности результатов при использовании вещественных типов данных разной точности.
create or replace table t_double (d double precision); create or replace table t_real(r real); insert into t_real values (1e+20); insert into t_real values (999.4); insert into t_double select * from t_real; select * from t_double; D - | 1.00000002004088e+020| | 999.400024414063|
create or replace table t1_double (d1 double precision); create or replace table t2_double (d2 double precision); insert into t2_double values (1e+20); insert into t2_double values (999.4); insert into t1_double select * from t2_double; select * from t1_double; D1 - | 1e+020| | 999.4|
create or replace table tst (r real); insert into tst values (12.35635); insert into tst values (98.61324); insert into tst values (1e+20); select ceil(r), floor(r) from tst; | 13| 12| | 99| 98| | 1.00000002004088e+020| 1.00000002004088e+020|
Из примера видно, что результат округления с недостатком оказался больше исходного значения. Для правильного результата нужно использовать тип данных DOUBLE
:
create or replace table tst (d double); insert into tst values (12.35635); insert into tst values (98.61324); insert into tst values (1e+20); select ceil(d), floor(d) from tst; | 13| 12| | 99| 98| | 1e+020| 1e+020|
Примечание
Для сравнения вещественных чисел с заданной точностью рекомендуется использовать следующую конструкцию:
select abs(a-b)/greatest(abs(a),abs(b)), abs(a-b) < = DBL_EPSILON * greatest(abs(a),abs(b)) from test_tbl;
где DBL_EPSILON
(или FLT_EPSILON
для FLOAT
) – заданная точность сравнения.
Пример:
create or replace table test_tbl(a float, b float); insert into test_tbl values(0.000585955393034, 0.000585955393035); insert into test_tbl values(0.00058595539303496, 0.00058595539303497); insert into test_tbl values(0.000585955393034964, 0.000585955393034965); insert into test_tbl values(0.000585955393034965444, 0.0005859553930349654446); select abs(a-b)/greatest(abs(a),abs(b)) relative_difference, abs(a-b) < = 1e-12 * greatest(abs(a),abs(b)) comparison_result from test_tbl; |relative_difference |comparison_result| |1.70673074399179e-12 |F | |1.68378683546467e-14 |T | |1.66528368342661e-15 |T | |0 |T | select abs(a-b)/greatest(abs(a),abs(b)) relative_difference, abs(a-b) < = 1e-14 * greatest(abs(a),abs(b)) comparison_result from test_tbl; |relative_difference |comparison_result| |1.70673074399179e-12 |F | |1.68378683546467e-14 |F | |1.66528368342661e-15 |T | |0 |T | select abs(a-b)/greatest(abs(a),abs(b)) relative_difference, abs(a-b) < = 1e-16 * greatest(abs(a),abs(b)) comparison_result from test_tbl; |relative_difference |comparison_result| |1.70673074399179e-12 |F | |1.68378683546467e-14 |F | |1.66528368342661e-15 |F | |0 |T |