2014 dxdy logo

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

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




Начать новую тему Ответить на тему На страницу Пред.  1, 2, 3, 4
 
 Re: Оператор round() в python 3
Сообщение24.10.2022, 20:30 
Заслуженный участник


20/08/14
11365
Россия, Москва
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 
Заслуженный участник


12/07/07
4485
[Над вашим последним сообщением, как найду время, подумаю. Спасибо.]

В 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 


31/08/22
183
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 
Заслуженный участник


12/07/07
4485
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 
Заслуженный участник


20/08/14
11365
Россия, Москва
GAA в сообщении #1567857 писал(а):
В этом варианте условие срабатывает при значении переменной цикла 29.
Вы правы, а моя симуляция кривая.

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 50 ]  На страницу Пред.  1, 2, 3, 4

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group