Я использую Firebird 3.0.4 (как в Windows, так и в Linux), и у меня есть следующая процедура, которая ясно демонстрирует мою проблему с числами с плавающей запятой, а также демонстрирует возможный обходной путь:
create or alter procedure test_float returns (res double precision,
res1 double precision,
res2 double precision)
as
declare variable z1 double precision;
declare variable z2 double precision;
declare variable z3 double precision;
begin
z1=15;
z2=1.1;
z3=0.49;
res=z1*z2*z3; /* one expects res to be 8.085, but internally, inside the procedure
it is represented as 8.084999999999.
The procedure-internal representation is repaired when then
res is sent to the output of the procedure, but the procedure-internal
representation (which is worng) impacts the further calculations */
res1=round(res, 2);
res2=round(round(res, 8), 2);
suspend;
end
На можно увидеть результат процедуры с:
select proc.res, proc.res1, proc.res2
from test_float proc
Результат
RES RES1 RES2
8,085 8,08 8,09
Но можно ожидать, что RES2 должен быть 8.09.
Хорошо видно, что внутреннее представление res содержит 8.0849999 (например, можно присвоить res сообщению об исключении, а затем вызвать это исключение), оно восстанавливается при выводе, но приводит к сбою вычислений, когда такая переменная используется в дальнейшем. расчеты.
RES2
демонстрирует восстановление: я всегда могу применить ROUND(..., 8)
для восстановления внутреннего представления. Я готов пойти с этим решением, но мой вопрос в том, является ли это приемлемым обходным путем (когда внешний ОКРУГ имеет строго менее 5 знаков после запятой) или есть лучший обходной путь.
Все мои тесты проходят с этим обходным путем, но ощущение плохое.
Конечно, я знаю тот минимум, который должен знать каждый программист о числах с плавающей запятой (об этом есть статья), и я знаю, что нельзя использовать double для бизнес-вычислений.