[Над вашим последним сообщением, как найду время, подумаю. Спасибо.]
В Python float — это binary64, т.е. Double. (Под win64, как правило, используется SSE/AVX. При этом, если компилятор выбирает SSE/AVX, то тип Extended это Double и FPU можно, как правило, использовать только во вставках на ассемблере или в линкуемых функциях. Примером может служить Delphi.). Под это и был заточен мой текст. Поэтому и сохранялись промежуточные вычисления в Double. И, конечно, округление при сохранении Double играет важнейшую роль.
C тестом я немного ошибся. Поэтому и не ловил. Но его легко подправить
var
i,a,b: integer; x: Double;
begin
for i:=0 to 100
do begin
a:=round(i+5/10); x:=i/100+5/1000; x:= 100*x; b:=round(x);
if a<>b then begin Writeln(i:3, x/100:27:20,' -> ',b/100:1:5); break; end;
end;
end.
И получить при выполнении на FPU:
54 0.5450000000000000710000 -> 0.55000. [Тестил до 50, чуток не хватило.]
И, конечно же, да, от приготовления значения в переменной изменится результат. В случае с 0.135 изменения не будет. Т.е.
x:= 0.135; x:=100*x; x:= round(x)/100; вернёт
0.14 (если x имеет тип Double), в случае
x:= 2.135 при выполнении на FPU вернёт
2.13, а не ожидаемое 2.14. (Число 2.135 взято просто как самое заезженное в Интернете.)
Из-за потери точности, которая может привести к представлению десятичных значений в виде чисел с плавающей запятой или выполнению арифметических операций со значениями с плавающей запятой, в некоторых случаях Round(Double, Int32, MidpointRounding) метод может не округлить значения в середине точки, как указано в параметре mode . Это показано в следующем примере, где 2,135 округляется до 2,13 вместо 2,14. Это происходит потому, что внутри метода умножается value на
, а операция умножения в этом случае страдает от потери точности.
Может и меньше можно подобрать, но это не столь важно, см. дальше.
Если сделать x0 Extended, а x оставить Double, то
x0 := 2.135; x:=100*x0; x:= round(x)/100; вернёт
2.14.
И в следующей модификации теста не будет срабатывать условие (для множителей 100, 1000, дальше пробовал с LX = 99999999)
var
i,a,b: Int64; x: Double;
x0: Extended;
LX: int64 = 9999999999;
f: Text;
begin
AssignFile(f, 'out.txt');
Rewrite(f);
i:= 0;
while i < LX
do begin
a:=round(i+5/10); x0:=i/100+5/1000; x:= 100*x0; b:=round(x);
if a<>b then begin Writeln(f, i:3, x0:27:20,' -> ',b/100:1:5); break; end;
inc(i);
end;
CloseFile(f);
end.
Возможно, можно придумать более тонкий тест для округления при использовании FPU. Тут главный недостаток в исходных данных для округления: они должны быть в некоторых случаях с избыточной точностью (Extended). Тогда округление при сохранении в Double будет срабатывать. Но вот, если использовать только binary64, то с округлением при помощи умножения, округления и последующего деления совсем плохо.
Поэтому, интересно, как Matlab c этим борется. Исходники для функции Round для Python 3 (built-in) и Matlab я найти не смог. Может кто-то поможет (не с исходником, так с алгоритмом)?