2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




На страницу Пред.  1, 2, 3, 4
 
 Re: Оператор round() в python 3
Сообщение24.10.2022, 20:30 
GAA в сообщении #1567481 писал(а):
Нет, надо x:=100*(i/100+5/1000); b:=round(x);
И разумеется это никак на работу не влияет, если остаёмся в extended переменных, в них регистры FPU сохраняются точно (без погрешностей), во всяком случае нормализованные и денормализованные числа и ноль (со всякими спецчислами вопрос отдельный).
Вот если переменные будут double или single, то да, в момент сохранения в переменную произведётся округление до 53/24 битов мантиссы и число из "с недостатком" может превратиться в "с избытком" и наоборот, зависит от отбрасываемых битов. И соответственно округляться тоже может потом по другому чем исходное.
GAA в сообщении #1567481 писал(а):
Всё остальное, что Вы написали я уже давно знаю. И тему я читал внимательно. И да общий вывод: нужно использовать символьные вычисления или близкие к ним. Но я же не об этом пишу на этой странице.
Тогда я не понимаю о чём Вы.
GAA в сообщении #1567484 писал(а):
Вы проверили? Опечатка? Ошибка?
Что тут проверять? В double? Зачем? FPU работает в extended, его и надо проверять.

Симуляция показывает что ошибка округления при сохранении результата умножения в регистр FPU должна возникать для любых степеней десятки как только количество значащих бит превышает 65, т.е. при умножении на 100 ошибка должна проявляться для чисел от 2^65/100=368934881474191032.335=0x4039A3D70A3D70A3D70A (точное значение 368934881474191032.3125). Почему правая цифра не 0xB непонятно, так дельфи округлила числовую константу. Но прямо проверить на дельфи это не удаётся, результат умножения превышает $2^{63}$ и при попытке его округлить команда FISTP вылетает по ошибке.
Проверил на асме:
x=368934881474191032.335000->368934881474191032.340000, M=0x4039A3D70A3D70A3D70B->0x4039A3D70A3D70A3D70B, 100M=0x40408000000000000000.6->0x40408000000000000000.8, R=0x40408000000000000001 (это симуляция), ошибка в несовпадении после округления чисел 100M.
На асме из числа 0x4039A3D70A3D70A3D70B умножением на целое 100 получилось именно число 0x40408000000000000001, т.е. ошибочное.
Меньшие числа должны округляться правильно.

 
 
 
 Re: Оператор round() в python 3
Сообщение25.10.2022, 18:59 
[Над вашим последним сообщением, как найду время, подумаю. Спасибо.]

В Python float — это binary64, т.е. Double. (Под win64, как правило, используется SSE/AVX. При этом, если компилятор выбирает SSE/AVX, то тип Extended это Double и FPU можно, как правило, использовать только во вставках на ассемблере или в линкуемых функциях. Примером может служить Delphi.). Под это и был заточен мой текст. Поэтому и сохранялись промежуточные вычисления в Double. И, конечно, округление при сохранении Double играет важнейшую роль.
C тестом я немного ошибся. Поэтому и не ловил. Но его легко подправить
Используется синтаксис Delphi
 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 взято просто как самое заезженное в Интернете.)
Microsoft на странице Math.Round[C#] писал(а):
Из-за потери точности, которая может привести к представлению десятичных значений в виде чисел с плавающей запятой или выполнению арифметических операций со значениями с плавающей запятой, в некоторых случаях Round(Double, Int32, MidpointRounding) метод может не округлить значения в середине точки, как указано в параметре mode . Это показано в следующем примере, где 2,135 округляется до 2,13 вместо 2,14. Это происходит потому, что внутри метода умножается value на $10^{digits}$, а операция умножения в этом случае страдает от потери точности.
Может и меньше можно подобрать, но это не столь важно, см. дальше.
Если сделать x0 Extended, а x оставить Double, то x0 := 2.135; x:=100*x0; x:= round(x)/100; вернёт 2.14.
И в следующей модификации теста не будет срабатывать условие (для множителей 100, 1000, дальше пробовал с LX = 99999999)
код: [ скачать ] [ спрятать ]
Используется синтаксис Delphi
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 я найти не смог. Может кто-то поможет (не с исходником, так с алгоритмом)?

 
 
 
 Re: Оператор round() в python 3
Сообщение26.10.2022, 16:47 
GAA в сообщении #1567731 писал(а):
Исходники для функции Round для Python 3 (built-in) и Matlab я найти не смог. Может кто-то поможет (не с исходником, так с алгоритмом)?

Надо понимать, что питон это прежде всего весь набор документов описывающих его поведение. А уж реализаций исполняющих эти документы великое множество.
Самый классический вот
https://github.com/python/cpython
А вот вроде и сама функция def __round__
https://github.com/python/cpython/blob/main/Lib/fractions.py?ysclid=l9pokocsoc823095454
double_round(double x, int ndigits)
https://github.com/python/cpython/blob/97e9cfa75a80b54a0630b7371f35e368a12749d1/Objects/floatobject.c

ПС: Читаю я Вас и не понимаю. Выяснено же, что корень зла это IEEE 754, отсюда и пляшет все поведение.

 
 
 
 Re: Оператор round() в python 3
Сообщение26.10.2022, 17:34 
Dmitriy40, я думаю ещё долго не поддержу вопрос тестирования точности умножения, если вообще поддержу, т.к. с этой тематикой не знаком, а изучать сейчас нет времени.

Если просто говорить о том, чтобы все переменные сделать Extended, то результат всем известен (и он ещё раз был озвучен на первой странице этой ветки).
Давайте я что-то совсем банальное напишу вокруг предложенного Вами теста. Я слегка переделаю это тест для простоты
Используется синтаксис Delphi
var
 i, a,b: Integer;
 x: Extended;
begin
 for i:= 29 to 999999
  do begin
      x:=i+5/10; a:=round(x);
      x:= x/100;
      x:= 100*x; b:=round(x);
      if a <> b then begin Writeln(i:3, x:27:20,' -> ',b/100:1:5); end;
     end;
end.
В этом варианте условие срабатывает при значении переменной цикла 29.
В x первоначально заносится 29.5 [4003EC00000000000000, а в a значение 30.
После деления x на 100 получаем значение с недостатком
3FFD970A3D70A3D70A3D (0.01001011100001010001111010111000010100011110101110000101000111101 или 0.29499999999999999998807377610265945122591801919043064117431640625)
[Если мы увеличим это число до 0.01001011100001010001111010111000010100011110101110000101000111111, то разность между этим числом и 0.295 будет приблизительно $4.2\cdot10^{-20}$. А разность 0.295 и найденного процессором будет приблизительно $1.2\cdot10^{-20}$. Т.е. процессор, ожидаемо, выбрал лучше.]
После последующего умножения на 100, x принимает значение 4003EBFFFFFFFFFFFFFF ( 29.49999999999999999826527652402319290558807551860809326171875)
В b заносится результат округления равный 29. Вроде всё естественно. Тут я мог немного ошибиться (или не всё скопировать), т.к. числа длинные. Но это не специально.

Matlab при вызове функции round округляет не число, реально находящееся в переменной, а значение отображаемое на экране. Об этом я писал на первой странице этой ветки. В этом он противоположен Python.
И 2.135 он округляет (в своём Matlab понимании) верно. У меня нет сейчас новой версии. В моей версии нет to even. Но сути это не меняет.
Используется синтаксис Matlab M
>> format long
>> format long
>> x = 2.135;
>> round(x, 2)
ans = 2.140000000000000

Конечно, даже в формате long нам не показывают все цифры числа. Поэтому требуется округлять не Double, а числа «с меньшей длиной мантиссы», но всё равно интересен алгоритм.

-- Wed 26.10.2022 17:16:50 --

Schrodinger's cat, спасибо за ссылку на исходник для Python! Вы чрезвычайно доброжелательны. Даже имена функций указали. Находится всё мгновенно! [Я там раньше лазил, но нужные файлы, увы, не нашёл.]

 
 
 
 Re: Оператор round() в python 3
Сообщение26.10.2022, 18:38 
GAA в сообщении #1567857 писал(а):
В этом варианте условие срабатывает при значении переменной цикла 29.
Вы правы, а моя симуляция кривая.

 
 
 [ Сообщений: 50 ]  На страницу Пред.  1, 2, 3, 4


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group