Общая рекомендация
Аргументы всех математических функций могут задаваться в виде <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+20| | 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+20| | 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| | 1e+20| 1e+20|
Из примера видно, что результат округления с недостатком оказался больше исходного значения. Для правильного результата нужно использовать тип данных 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+20| 1e+20|
Примечание
Для сравнения вещественных чисел с заданной точностью рекомендуется использовать следующую конструкцию:
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 |