2014 dxdy logo

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

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




Начать новую тему Ответить на тему На страницу Пред.  1, 2, 3, 4  След.
 
 Re: Оператор round() в python 3
Сообщение20.10.2022, 19:19 
Аватара пользователя


11/06/12
10390
стихия.вздох.мюсли
На самом деле мы с вами, GAA рассматриваем разное. Я рассказываю, как ведёт себя Mathematica с примерами кода на Wolfram Language, вы же со своей стороны изучаете ответы Wolfram|Alpha на запросы, сформулированные этаким полукодом, который эта система сначала интерпретирует, переводя предположительно в команды Wolfram Language, затем выполняет эти команды и выдаёт результат. Но промежуточный результат интерпретации она не показывает. И даже несмотря на то, что я могу ввести ваши запросы в Mathematica, где есть функционал интерпретации запросов на естественном языке и перевода их на Wolfram Language (уже с получением этого перевода и доступностью его дальнейшего редактирования), мы не можем быть полностью уверены, что две системы в данных условиях делают в точности одно и то же. Но так даже интереснее.

 Профиль  
                  
 
 Re: Оператор round() в python 3
Сообщение20.10.2022, 19:52 
Заслуженный участник


12/07/07
4522
Я писал довольно коряво, но не по тому, что не уважаю читателей, а потому, что не владею Mathematica и думал, что по текстам недостатки словесного описания будут преодолены. Прошу прощение за корявость сообщений.

На искусственный тест я дал универсальный стандартный ответ: преобразовывать к десятичным дробям в самый последний момент. (И это не зависит от системы. Такую же рекомендацию дадут и в случае Matlab.) Но функция round должна работать и с десятичными дробями. И я постарался сформулировать проблему: если аргумент скалярный, то в Wolfram Alpha функция round работает ожидаемо, но если аргумент список, то нет:
round(0.35, 0.1) вернёт 0.4, и round(0.45, 0.1) вернёт 0.4, но
round({0.35, 0.45}, 0.1) вернёт {0.3, 0.4}.
(Это почти минимальный демонстрационный пример. Минимальным будет список из одного элемента, он приводился выше.)
Эту проблему можно сформулировать и иначе. В Wolfram Alpha
round(0.05 + 3/10, 0.1) вернёт 0.4, но
Table[round(0.05 + i/10, 0.1), {i,3,3}] вернёт {0.3}.
(Это минимальный демонстрационный пример.)

Может в Mathematica, в отличие от Wolfram Alpha, такие примеры ничего и не покажут. Тогда и проблемы нет. (У меня сейчас нет возможности проверить в Wolfram Mathematica.) Если примеры подтвердятся, то хотя в случае с round это может и не важно, но где-то ещё может проявиться. Т.е. вопрос скорее о работе Mathematica с десятичными дробями.

P.S. Я и Wolfram Alpha не использую, третий раз в жизни заглянул в эту систему. И то, чтобы дать стандартный ответ, но потом стало интересно, когда увидел, что проблемы связаны со списками. Может в тему пользователи Wolfram Mathematica заглянут и всё пояснят.

 Профиль  
                  
 
 Re: Оператор round() в python 3
Сообщение21.10.2022, 00:15 
Заслуженный участник


12/07/07
4522
Wolfram Mathematica 12.0:
Код:
In:= Round[0.35,0.1]
Out:= 0.3
In:=Round[5/100+3/10,1/10]/1.0
Out:= 0.4
Т.е. в 12 версии даже со скалярным десятичным аргументом выполняется некорректно.

 Профиль  
                  
 
 Re: Оператор round() в python 3
Сообщение21.10.2022, 02:54 
Заслуженный участник


12/07/07
4522
Попробовал в 13 версии. Результат тот же. Округлять десятичную дробь получается верно, только в случае округления до целого (по проведенным проверкам).

 Профиль  
                  
 
 Re: Оператор round() в python 3
Сообщение23.10.2022, 16:52 
Заслуженный участник


12/07/07
4522
По поводу неточности двоичного представления в форматах с плавающей точкой.
Округления до целого в FPU (типичное его использование под Windows — при генерации 32-битного кода) выполняется во время сохранения вершины стека в памяти инструкцией FISTP. Режим округления задаётся битами в Control Word. В Borland Delphi 5 в Control Word занесены значения соответствующие режиму округления к ближайшему, а в случае значения посередине — к чётному (\Source\RTL\Sys\system.pas) [В случае необходимости сменить режим округления в некоторой функции перед её завершением происходит восстановление режима округления.]
Используется синтаксис ASM
procedure       _ROUND;
asm
        { ->    FST(0)  Extended argument       }
        { <-    EDX:EAX Result                  }

        SUB     ESP,8
        FISTP   qword ptr [ESP]
        FWAIT
        POP     EAX
        POP     EDX
end;
В Borland Delphi 5 значения типа Longint возвращаются через регистры EAX и EAX. «Функция» выделяет дополнительно два двойных слова на стеке (SUB ESP,8), а затем перед «завершением» восстанавливает состояние стека (POP EAX, POP EDX).
Следующий текст тестит округление при использовании FPU
Код:
var
i: Integer;
d1, d1m, d2, d2m, d3, d3m : Double;
f: Text;
begin
i: Integer;
d1, d1m, d2, d2m, d3, d3m : Double;
f: Text;
begin
AssignFile(f, 'out.txt');
Rewrite(f);
for i:= 0 to 50
  do begin
       d1:= 0.05+i/10;        d1m:= 10*d1;
       d2:= 0.0005+i/1000;    d2m:= 1000*d2;
       d3:= 0.00005+i/10000;  d3m:= 10000*d3;
       writeln(f,d1:4:2,' -> ',Round(d1m)/10:3:1,';  ',d2:6:4,' -> ',Round(d2m)/1000:5:3,';  ',d3:7:5,' -> ',Round(d3m)/10000:6:4);
     end;
CloseFile(f)
end.
Содержимое файла out.txt (при выполнении на процессоре AMD, на Intel не пробовал):
код: [ скачать ] [ спрятать ]
  1. 0.05 -> 0.0; 0.0005 -> 0.000; 0.00005 -> 0.0000 
  2. 0.15 -> 0.2; 0.0015 -> 0.002; 0.00015 -> 0.0001 
  3. 0.25 -> 0.2; 0.0025 -> 0.002; 0.00025 -> 0.0002 
  4. 0.35 -> 0.4; 0.0035 -> 0.004; 0.00035 -> 0.0004 
  5. 0.45 -> 0.4; 0.0045 -> 0.004; 0.00045 -> 0.0004 
  6. 0.55 -> 0.6; 0.0055 -> 0.006; 0.00055 -> 0.0006 
  7. 0.65 -> 0.6; 0.0065 -> 0.006; 0.00065 -> 0.0006 
  8. 0.75 -> 0.8; 0.0075 -> 0.008; 0.00075 -> 0.0008 
  9. 0.85 -> 0.8; 0.0085 -> 0.008; 0.00085 -> 0.0008 
  10. 0.95 -> 1.0; 0.0095 -> 0.010; 0.00095 -> 0.0010 
  11. 1.05 -> 1.0; 0.0105 -> 0.010; 0.00105 -> 0.0010 
  12. 1.15 -> 1.2; 0.0115 -> 0.012; 0.00115 -> 0.0012 
  13. 1.25 -> 1.2; 0.0125 -> 0.012; 0.00125 -> 0.0012 
  14. 1.35 -> 1.4; 0.0135 -> 0.014; 0.00135 -> 0.0014 
  15. 1.45 -> 1.4; 0.0145 -> 0.014; 0.00145 -> 0.0014 
  16. 1.55 -> 1.6; 0.0155 -> 0.016; 0.00155 -> 0.0016 
  17. 1.65 -> 1.6; 0.0165 -> 0.016; 0.00165 -> 0.0016 
  18. 1.75 -> 1.8; 0.0175 -> 0.018; 0.00175 -> 0.0018 
  19. 1.85 -> 1.8; 0.0185 -> 0.018; 0.00185 -> 0.0018 
  20. 1.95 -> 2.0; 0.0195 -> 0.020; 0.00195 -> 0.0020 
  21. 2.05 -> 2.0; 0.0205 -> 0.020; 0.00205 -> 0.0020 
  22. 2.15 -> 2.2; 0.0215 -> 0.022; 0.00215 -> 0.0022 
  23. 2.25 -> 2.2; 0.0225 -> 0.022; 0.00225 -> 0.0022 
  24. 2.35 -> 2.4; 0.0235 -> 0.024; 0.00235 -> 0.0024 
  25. 2.45 -> 2.4; 0.0245 -> 0.024; 0.00245 -> 0.0024 
  26. 2.55 -> 2.6; 0.0255 -> 0.026; 0.00255 -> 0.0026 
  27. 2.65 -> 2.6; 0.0265 -> 0.026; 0.00265 -> 0.0026 
  28. 2.75 -> 2.8; 0.0275 -> 0.028; 0.00275 -> 0.0028 
  29. 2.85 -> 2.8; 0.0285 -> 0.028; 0.00285 -> 0.0028 
  30. 2.95 -> 3.0; 0.0295 -> 0.030; 0.00295 -> 0.0030 
  31. 3.05 -> 3.0; 0.0305 -> 0.030; 0.00305 -> 0.0031 
  32. 3.15 -> 3.2; 0.0315 -> 0.032; 0.00315 -> 0.0032 
  33. 3.25 -> 3.2; 0.0325 -> 0.032; 0.00325 -> 0.0032 
  34. 3.35 -> 3.4; 0.0335 -> 0.034; 0.00335 -> 0.0034 
  35. 3.45 -> 3.4; 0.0345 -> 0.034; 0.00345 -> 0.0034 
  36. 3.55 -> 3.6; 0.0355 -> 0.036; 0.00355 -> 0.0036 
  37. 3.65 -> 3.6; 0.0365 -> 0.036; 0.00365 -> 0.0036 
  38. 3.75 -> 3.8; 0.0375 -> 0.038; 0.00375 -> 0.0038 
  39. 3.85 -> 3.8; 0.0385 -> 0.038; 0.00385 -> 0.0038 
  40. 3.95 -> 4.0; 0.0395 -> 0.040; 0.00395 -> 0.0040 
  41. 4.05 -> 4.0; 0.0405 -> 0.040; 0.00405 -> 0.0040 
  42. 4.15 -> 4.2; 0.0415 -> 0.042; 0.00415 -> 0.0042 
  43. 4.25 -> 4.2; 0.0425 -> 0.042; 0.00425 -> 0.0042 
  44. 4.35 -> 4.4; 0.0435 -> 0.044; 0.00435 -> 0.0044 
  45. 4.45 -> 4.4; 0.0445 -> 0.044; 0.00445 -> 0.0044 
  46. 4.55 -> 4.6; 0.0455 -> 0.046; 0.00455 -> 0.0046 
  47. 4.65 -> 4.6; 0.0465 -> 0.046; 0.00465 -> 0.0046 
  48. 4.75 -> 4.8; 0.0475 -> 0.048; 0.00475 -> 0.0048 
  49. 4.85 -> 4.8; 0.0485 -> 0.048; 0.00485 -> 0.0048 
  50. 4.95 -> 5.0; 0.0495 -> 0.050; 0.00495 -> 0.0050 
  51. 5.05 -> 5.0; 0.0505 -> 0.050; 0.00505 -> 0.0050 
Видно, что с увеличением позиции вправо от десятичной точки на 3, по сравнению с округлением до целого, начинают проявляться «сбои» округления. В частности, 0.00015 округляется до 0.0001, а не до ожидаемого 0.0002. Выглядит так, что Extended (в который преобразуется Double при загрузке в FPU и в котором выполняются все операции в FPU, в том числе умножение) недостаточно для корректного вычисления произведения:
10000*(0.00005+1/1000) в формате Double будет равно 3FF7FFFFFFFFFFFF =
00111111111 10111111111111111111111111111111111111111111111111111
1.5 в формате Double будет равно 3FF8000000000000 =
00111111111 11000000000000000000000000000000000000000000000000000.
Видно, что мантисса первого числа меньше мантиссы второго числа, поэтому округление и приводит к 0.0001.

В 64-битном коде под Windows, который используется в пакетах или генерируется компиляторами, как правило, используется SSE/AVX (нет поддержки Extended). Результат выполнения (на процессоре AMD), скомпилированного под Win64 в Embarcadero Delphi XE7 предыдущего исходного текста (файл out.txt):
код: [ скачать ] [ спрятать ]
  1. 0.05 -> 0.0; 0.0005 -> 0.000; 0.00005 -> 0.0000 
  2. 0.15 -> 0.2; 0.0015 -> 0.002; 0.00015 -> 0.0002 
  3. 0.25 -> 0.2; 0.0025 -> 0.002; 0.00025 -> 0.0002 
  4. 0.35 -> 0.4; 0.0035 -> 0.004; 0.00035 -> 0.0004 
  5. 0.45 -> 0.4; 0.0045 -> 0.005; 0.00045 -> 0.0004 
  6. 0.55 -> 0.6; 0.0055 -> 0.006; 0.00055 -> 0.0006 
  7. 0.65 -> 0.6; 0.0065 -> 0.007; 0.00065 -> 0.0006 
  8. 0.75 -> 0.8; 0.0075 -> 0.008; 0.00075 -> 0.0008 
  9. 0.85 -> 0.8; 0.0085 -> 0.008; 0.00085 -> 0.0008 
  10. 0.95 -> 1.0; 0.0095 -> 0.010; 0.00095 -> 0.0010 
  11. 1.05 -> 1.0; 0.0105 -> 0.010; 0.00105 -> 0.0010 
  12. 1.15 -> 1.2; 0.0115 -> 0.012; 0.00115 -> 0.0012 
  13. 1.25 -> 1.2; 0.0125 -> 0.012; 0.00125 -> 0.0012 
  14. 1.35 -> 1.4; 0.0135 -> 0.014; 0.00135 -> 0.0013 
  15. 1.45 -> 1.4; 0.0145 -> 0.014; 0.00145 -> 0.0014 
  16. 1.55 -> 1.6; 0.0155 -> 0.016; 0.00155 -> 0.0016 
  17. 1.65 -> 1.6; 0.0165 -> 0.016; 0.00165 -> 0.0016 
  18. 1.75 -> 1.8; 0.0175 -> 0.018; 0.00175 -> 0.0017 
  19. 1.85 -> 1.8; 0.0185 -> 0.018; 0.00185 -> 0.0018 
  20. 1.95 -> 2.0; 0.0195 -> 0.020; 0.00195 -> 0.0020 
  21. 2.05 -> 2.0; 0.0205 -> 0.020; 0.00205 -> 0.0020 
  22. 2.15 -> 2.2; 0.0215 -> 0.022; 0.00215 -> 0.0022 
  23. 2.25 -> 2.2; 0.0225 -> 0.022; 0.00225 -> 0.0023 
  24. 2.35 -> 2.3; 0.0235 -> 0.024; 0.00235 -> 0.0024 
  25. 2.45 -> 2.4; 0.0245 -> 0.024; 0.00245 -> 0.0024 
  26. 2.55 -> 2.6; 0.0255 -> 0.026; 0.00255 -> 0.0026 
  27. 2.65 -> 2.6; 0.0265 -> 0.026; 0.00265 -> 0.0026 
  28. 2.75 -> 2.8; 0.0275 -> 0.028; 0.00275 -> 0.0028 
  29. 2.85 -> 2.8; 0.0285 -> 0.028; 0.00285 -> 0.0028 
  30. 2.95 -> 2.9; 0.0295 -> 0.030; 0.00295 -> 0.0030 
  31. 3.05 -> 3.0; 0.0305 -> 0.030; 0.00305 -> 0.0031 
  32. 3.15 -> 3.2; 0.0315 -> 0.032; 0.00315 -> 0.0032 
  33. 3.25 -> 3.2; 0.0325 -> 0.032; 0.00325 -> 0.0032 
  34. 3.35 -> 3.4; 0.0335 -> 0.034; 0.00335 -> 0.0034 
  35. 3.45 -> 3.4; 0.0345 -> 0.034; 0.00345 -> 0.0034 
  36. 3.55 -> 3.6; 0.0355 -> 0.036; 0.00355 -> 0.0036 
  37. 3.65 -> 3.6; 0.0365 -> 0.036; 0.00365 -> 0.0036 
  38. 3.75 -> 3.8; 0.0375 -> 0.038; 0.00375 -> 0.0038 
  39. 3.85 -> 3.8; 0.0385 -> 0.038; 0.00385 -> 0.0038 
  40. 3.95 -> 4.0; 0.0395 -> 0.040; 0.00395 -> 0.0039 
  41. 4.05 -> 4.0; 0.0405 -> 0.040; 0.00405 -> 0.0040 
  42. 4.15 -> 4.1; 0.0415 -> 0.042; 0.00415 -> 0.0042 
  43. 4.25 -> 4.2; 0.0425 -> 0.042; 0.00425 -> 0.0042 
  44. 4.35 -> 4.4; 0.0435 -> 0.044; 0.00435 -> 0.0044 
  45. 4.45 -> 4.4; 0.0445 -> 0.044; 0.00445 -> 0.0044 
  46. 4.55 -> 4.6; 0.0455 -> 0.046; 0.00455 -> 0.0045 
  47. 4.65 -> 4.6; 0.0465 -> 0.046; 0.00465 -> 0.0046 
  48. 4.75 -> 4.8; 0.0475 -> 0.048; 0.00475 -> 0.0048 
  49. 4.85 -> 4.8; 0.0485 -> 0.048; 0.00485 -> 0.0048 
  50. 4.95 -> 5.0; 0.0495 -> 0.050; 0.00495 -> 0.0049 
  51. 5.05 -> 5.0; 0.0505 -> 0.050; 0.00505 -> 0.0050 
Уже при округлении до десятых наблюдается «неожиданные» результаты:
2.35->2.3; 2.95->2.9; 4.15->4.1.
При округлении до сотых:
.0045->.005; .0065->.007.
При округлении до тысячных:
.00135->.0013; .00175->.0017; .00225->.0023; .00305->.0031 и др.
Напрашивается вывод: если бы для умножения был использован binary128 (аппаратный, например, Itanium или эмуляция на регистрах общего назначения), то можно было бы округлять до тысячных или даже до больших знаков после запятой.

Помимо преобразования десятичных к рациональным в Wolfram Mathematica можно задать точность. Не прикидывал, а просто взял 50 (Mathematica 12):
Код:
Table[{.00005 + i/10000,  Round[N[10000*(.00005`50 + i/10000)]]/10000.0}, {i, 0, 50}] // Column
{{{0.00005, 0.}},
{{0.00015, 0.0002}},
{{0.00025, 0.0002}},
{{0.00035, 0.0004}},
{{0.00045, 0.0004}},
{{0.00055, 0.0006}},
{{0.00065, 0.0006}},
{{0.00075, 0.0008}},
{{0.00085, 0.0008}},
{{0.00095, 0.001}},
{{0.00105, 0.001}},
{{0.00115, 0.0012}},
{{0.00125, 0.0012}},
{{0.00135, 0.0014}},
{{0.00145, 0.0014}},
{{0.00155, 0.0016}},
{{0.00165, 0.0016}},
{{0.00175, 0.0018}},
{{0.00185, 0.0018}},
{{0.00195, 0.002}},
{{0.00205, 0.002}},
{{0.00215, 0.0022}},
{{0.00225, 0.0022}},
{{0.00235, 0.0024}},
{{0.00245, 0.0024}},
{{0.00255, 0.0026}},
{{0.00265, 0.0026}},
{{0.00275, 0.0028}},
{{0.00285, 0.0028}},
{{0.00295, 0.003}},
{{0.00305, 0.003}},
{{0.00315, 0.0032}},
{{0.00325, 0.0032}},
{{0.00335, 0.0034}},
{{0.00345, 0.0034}},
{{0.00355, 0.0036}},
{{0.00365, 0.0036}},
{{0.00375, 0.0038}},
{{0.00385, 0.0038}},
{{0.00395, 0.004}},
{{0.00405, 0.004}},
{{0.00415, 0.0042}},
{{0.00425, 0.0042}},
{{0.00435, 0.0044}},
{{0.00445, 0.0044}},
{{0.00455, 0.0046}},
{{0.00465, 0.0046}},
{{0.00475, 0.0048}},
{{0.00485, 0.0048}},
{{0.00495, 0.005}},
{{0.00505, 0.005}}}

 Профиль  
                  
 
 Re: Оператор round() в python 3
Сообщение23.10.2022, 17:33 
Заслуженный участник


20/08/14
11787
Россия, Москва

(Оффтоп)

GAA
Куда Вы торопитесь?
Почему не оформить код не code, а syntax, ведь дельфи там есть?
Опечатки, "возвращаются через регистры EAX и EAX" вместо EDX, в коде явное дублирование первых нескольких строк, "10000*(0.00005+1/1000)" нуля не хватает ...

GAA в сообщении #1567421 писал(а):
при выполнении на процессоре AMD, на Intel не пробовал
Если бы обнаружилась разница в выполнении float операций между процессорами, то это сенсация, ведь они оба должны реализовывать IEEE-754. Так что ИМХО оговорка лишняя, проблемы заложены в сами форматы чисел,а не производителя процессора.

GAA в сообщении #1567421 писал(а):
Выглядит так, что Extended (в который преобразуется Double при загрузке в FPU и в котором выполняются все операции в FPU, в том числе умножение) недостаточно для корректного вычисления произведения:
Дык недостаточно мантиссы любой длины, хоть триллион битов, там же период повторения (как для любого рационального числа за малым исключением знаменателей только из степени двойки).
Ну и про операции в FPU Вы не совсем правы, хотя пруф я вряд ли найду, но давным давно помню что в FPU используются дополнительные 4 бита (сверх явных 64 мантиссы) именно для более точного округления результатов умножений (да и сложений). Но в данном случае их всё равно не хватит, сколько бы их ни было.
Плюс, есть зависимость от порядка сложений слагаемых, из-за этого даже суммирование рядов довольно нетривиальная задача, для минимизации ошибок надо (как один из простых вариантов) складывать от наименьшего числа к наибольшему в порядке возрастания модуля чисел. Во что скомпилился Ваш код не показано, а это тоже в принципе может добавлять ошибок округлений.

 Профиль  
                  
 
 Re: Оператор round() в python 3
Сообщение23.10.2022, 18:44 
Заслуженный участник


12/07/07
4522
Спасибо за указание опечаток.
Dmitriy40 в сообщении #1567427 писал(а):
Дык недостаточно мантиссы любой длины, хоть триллион битов, там же период повторения
Это непонятно к чему. Нам нужно получить такой результат, чтобы двоичное представление результата умножения совпало с двоичным представлением в формате Double произведения. Например двоичное представление 1.5 в формате Double должно быть равным двоичному представлению в формате Double 100*0.015. [Или чтобы отклонение было в нужную сторону.]
Dmitriy40 в сообщении #1567427 писал(а):
Ну и про операции в FPU Вы не совсем правы, хотя пруф я вряд ли найду, но давным давно помню что в FPU используются дополнительные 4 бита (сверх явных 64 мантиссы) именно для более точного округления результатов умножений.
Именно это тесты в моём сообщении и демонстрируют.
Dmitriy40 в сообщении #1567427 писал(а):
Но в данном случае их всё равно не хватит,
Как видно из тестов, это зависит от цифры до которой надо округлять. Если до сотой, то хватает. Если до тысячной, то не хватает.

А к чему Вы это всё написали. Что-то в тестах не так? (Кроме двух трех опечаток.)

-- Sun 23.10.2022 17:47:43 --

Dmitriy40 в сообщении #1567427 писал(а):
Так что ИМХО оговорка лишняя
Да, лишняя. Вставил, чтобы вопросы не задавали и не было обсуждения не по делу. Но оказалось зря (не получилось избежать).

 Профиль  
                  
 
 Re: Оператор round() в python 3
Сообщение23.10.2022, 20:29 
Заслуженный участник


20/08/14
11787
Россия, Москва
GAA
Во первых в дельфи (во всяком случае в старых версиях) есть тип extended, именно родной для FPU. Так что "(в который преобразуется Double при загрузке в FPU" вовсе не обязательно реализуется, вполне можно работать сразу в extended без преобразования из double в него.
Во вторых любая дробь $a/b$ при $b \ne 2^k$ не представима точно в форматах с плавающей точкой с любой разрядностью мантиссы. В частности не представима и любая дробь $5/10^{k>1}$, потому её округление будет зависеть от двоичных (и десятичных) цифр слева (подчёркиваю, слева, не только справа!) от округляемого двоичного (десятичного) разряда - в зависимости от них мантисса будет или ...5000xxx или ...4999xxx (в десятичном виде), которые округляются принципиально по разному. Как именно получать такую дробь уже не столь принципиально.
В третьих даже extended (80 бит) точности не хватает для округления даже до сотых (заодно это и пример с extended форматом):
Используется синтаксис Delphi
var     i,a,b:integer; x:extended;
begin
        for i:=0 to 999 do begin
                a:=round(i+5/10.0); x:=i/100.0+5/1000.0; b:=round(100.0*x);
                if a<>b then begin Writeln(x:25:25,' -> ',b/100.0:1:5); break; end;
        end;
end.
Вывод:
0.1350000000000000000000000 -> 0.13000
Заметьте, условие некорректности округления проверяется в целых, погрешности вывода writeln роли не играют (в отличии от ваших логов).
И да, я проверил асм код, все операции выполняются в FPU, округление производится командой fistp и все нецелые константы представлены тоже в extended формате (а такие небольшие целые представимы точно и в double и в single).
Хотите поиграйтесь с округлением до тысячных и далее. Я в этом никакого смысла не вижу, закономерность будете искать долго ... А она просто в форматах использованных чисел, о чём было сказано уже давно.
В четвёртых понятие "округление до сотых" вообще плохо формализовано в данной теме, есть огромная разница округлить 0.135 или 999283394391736.135 или 29083902183092839021839217389273.135. Попробуйте округлить до сотых последнее число или второе (но его в double), ага. :facepalm:
В итоге непонятно чего Вы хотите добиться или выяснить.
Ну и в пятых пакеты математики могут использовать не аппаратные типы данных, а плавающие типы с основанием 10 вместо двух (именно ради удобства человека!) и тогда все игрища с выяснением точности округления в них изначально бессмысленны. Ну и что что они аппаратно не поддерживаются, высокой скорости расчётов от этих пакетов вроде и не требуют, а вот точность очень даже. Кстати это можно и проверить, взять число представимое точно в двоичной мантиссе, но не в десятичной (с ограниченным количеством цифр) и посмотреть как оно округлится при выводе или как хранится.

 Профиль  
                  
 
 Re: Оператор round() в python 3
Сообщение23.10.2022, 21:07 
Заслуженный участник


12/07/07
4522
У меня в Delphi 5 в следующем коде ни разу не срабатывает условие
Используется синтаксис Delphi
var
 i,a,b:integer; x: Double;
begin
 for i:= 0 to 999999999
  do begin
      a:=round(i+5/10); x:=100*(i/100+5/1000); b:=round(x);
      if a<>b then begin Writeln(x:25:25,' -> ',b/100:1:5); break; end;
     end;
end.

Почему Double? Просто потому, что сама Intel предлагала использовать переменные типа Extended только для промежуточных вычислений. И всякие системы инженерных расчётов (Matlab/Octave, Mathcad,... ) используют переменные типа Dauble. [При компиляции Delphi кода под win64 имя Extended эквивалентно Double. System.Extended: «On Intel 64-bit Windows systems <...> the System.Extended type is an alias for System.Double, which is only 8 bytes.»]
Dmitriy40 в сообщении #1567446 писал(а):
Ну и в пятых пакеты математики могут использовать не аппаратные типы данных
Они не используют аппаратные float за исключением сохранения данных в файл, вызова сторонних библиотек, ну и ещё в некоторых исключительных случаях. Но вот увеличение точности позволило в Mathematica округлять, см. пример выше. Ради Matlaba и Mathematica и был задуман тест.

-- Sun 23.10.2022 20:09:11 --

Dmitriy40 в сообщении #1567446 писал(а):
В четвёртых понятие "округление до сотых" вообще плохо формализовано в данной теме, есть огромная разница округлить 0.135 или 999283394391736.135
Я пишу о типичных случаях, а Вы шутите.

-- Sun 23.10.2022 20:20:04 --

Dmitriy40 в сообщении #1567446 писал(а):
В итоге непонятно чего Вы хотите добиться или выяснить
Ещё пытался понять следующие высказывания:
Dmitriy40 в сообщении #1566981 писал(а):
Если плюнуть нельзя (что необходимо обосновать), то есть много разных методов более точных вычислений <...> кончая своими типами float
photon в сообщении #1566962 писал(а):
Например, число $2.675$ хранится как примерно $2.67499999999999982236431605997495353221893310546875$, поэтому округление до двух знаков даст $2.67$, а не $2.68$, как должно бы быть по правилам округления
Интерпретацию первого привел (можно для умножения binary128 попробовать), а второе так и не понял (если FPU использовать, то можно).

 Профиль  
                  
 
 Re: Оператор round() в python 3
Сообщение23.10.2022, 22:18 
Заслуженный участник


20/08/14
11787
Россия, Москва
GAA в сообщении #1567451 писал(а):
У меня в Delphi 5 в следующем коде ни разу не срабатывает условие
А с чего ему срабатывать если все операции в целых? И деление нацело 5/10 даёт разумеется ровно 0. Думаете я просто так ставил везде .0 у чисел? ;-)
GAA в сообщении #1567451 писал(а):
Я пишу о типичных случаях, а Вы шутите.
Я не то чтобы шучу, скорее показываю недостаточную ясность чего Вы добиваетесь. Ведь всё понятно давным давно, ну вот хранятся так числа, что не дают точных дробей (за редким исключением). И на практике это может вылезти совершенно где угодно, не только при округлении небольших чисел до какого-то знака справа от запятой (слева тоже может вылезти). Недаром про точность вычислений с плавающей точкой научные статьи пишут и вопрос далеко не закрыт (но конечно не столь простой как здесь, а чуть посложнее).
GAA в сообщении #1567451 писал(а):
Интерпретацию первого привел (можно для умножения binary128 попробовать)
Так и не поняли ... Вот было число 0x4005666666666666 в double, если к нему приписать хоть 100500 (но кратно четырём) двоичных цифр справа, то оно так и останется с тучей шестёрок, ничего от этого не изменится, и так и будет 2.675 с недостачей, т.е. 2.674999999xxx. Хоть сколько групп по 4 бита добавьте, недостача никуда не денется. И после умножения на 100 недостача не исчезнет! А значит правило округления для 5-ки так и не сработает! Хоть триллион битов добавьте справа.
Другое дело что добавив не кратно 4 бита, а например всего 1 бит, получим не .....ACCCCC, а уже ......ACCCCD - потому что следующий бит (за границей мантиссы) будет единичным и округлится уже до следующего числа (и даже если вдруг в конкретно этом случае там и будет комбинация 1000, то легко подобрать пример без такой комбинации). Добавка двух бит даст не .....599999, а ......59999A по той же причине. Добавка трёх бит снова даст ....B33333. И так по кругу каждые 4 бита, сколько бы их ни было.
Легко заметить что два варианта из 4-х дадут правильное округление, т.е. дают число с избытком, а два варианта дают неправильное округление, т.е. число с недостатком. И никакой binary128 или binary128000000 от этого гарантированно не спасёт, всё равно будут ошибки, не на этом числе, так на других.
GAA в сообщении #1567451 писал(а):
а второе так и не понял (если FPU использовать, то можно).
Выше я показал пример когда именно FPU неправильно округляет число всего лишь 0.135, до двух знаков. Так что нет, в общем случае нельзя, даже хоть с FPU, хоть с binary128, хоть с binary128000000.

 Профиль  
                  
 
 Re: Оператор round() в python 3
Сообщение23.10.2022, 22:33 
Заслуженный участник


12/07/07
4522
Dmitriy40 в сообщении #1567458 писал(а):
А с чего ему срабатывать если все операции в целых? И деление нацело 5/10 даёт разумеется ровно 0. Думаете я просто так ставил везде .0 у чисел?
Результат деления целых чисел и в Pascal, и Delphi есть действительное. Попробуйте присвоить целому результат 5/10.
Dmitriy40 в сообщении #1567458 писал(а):
Выше я показал пример когда именно FPU неправильно округляет число всего лишь 0.135, до двух знаков.
Воспользуйтесь Double. Вы или не улавливаете контекст, или я слишком много хочу от читателя.

-- Sun 23.10.2022 21:47:09 --

GAA в сообщении #1567466 писал(а):
будет 2.675 с недостачей, т.е. 2.674999999xxx.
Нет. Двоичное представление 2.674999999xxx — это и есть для процессора 2.675. И если умножить на 100 так, чтобы все биты мантиссы совпали бы с двоичным представлением 267.5, то округлиться FPU к 268.

 Профиль  
                  
 
 Re: Оператор round() в python 3
Сообщение23.10.2022, 23:51 
Заслуженный участник


12/07/07
4522
В комментариях приведены значения [переменных] после выполнения строки. Желающие на калькуляторе могут проверить.
Delphi 5:
код: [ скачать ] [ спрятать ]
Используется синтаксис Delphi
var
 x: Double;
 i: Integer;
begin
 x:= 0.135; {3FC147AE147AE148 = 0.1350000000000000088817841970012523233890533447265625}
 x:= 100*x; {402B000000000000 = 13.500000000000000000000000000000000000000000000000000}
 i:= round(x); {i = 14}
 x:= i/100; {3FC1EB851EB851EC = 0.14000000000000001332267629550187848508358001708984375}
end.
{Вычисления}
 mov [x], $147ae148
 mov [x + $4], $3fc147ae
 fld dword ptr [PROGRAM + $68]
 fmul qword ptr [x]
 fstp qword ptr [x]
 wait
 fld qword ptr [x]
 call @ROUND
 mov [i],eax
 fild dword ptr [i]
 fdiv dword ptr [PROGRAM + $68]
 fstp qword ptr [x]
 wait
Используется синтаксис Delphi
var
 x: Double;
 i: Integer;
begin
 x:= 2.6749999999999998223643160599749535322189331054687;
 {4005666666666666}
 x:= 100*x;
 {4070B8000000000000 = 267.50000000000000000000000000000000000000000000000}
 i:= round(x);
 {i = 268}
 x:= i/100;
 {400570A3D70A3D71 = 2.680000000000000159872115546022541821002960205078125}
end.

 Профиль  
                  
 
 Re: Оператор round() в python 3
Сообщение24.10.2022, 01:27 
Заслуженный участник


20/08/14
11787
Россия, Москва
GAA в сообщении #1567466 писал(а):
Результат деления целых чисел и в Pascal, и Delphi есть действительное.
Правы, это я с чем-то спутал (возможно с C).
GAA в сообщении #1567466 писал(а):
Воспользуйтесь Double.
Прошу:
Используется синтаксис Delphi
var     i,a,b:integer; x:double;
begin
        for i:=0 to 99 do begin
                a:=round(i+5/10); x:=i/100+5/1000; b:=round(100*x);
                if a<>b then begin Writeln(x:25:25,' -> ',b/100.0:1:5); break; end;
        end;
end.
Вывод:
0.0050000000000000000000000 -> 0.01000
Все операции по прежнему в FPU.
С single ещё забавнее:
Используется синтаксис Delphi
var     i,a,b:integer; x:double;
begin
        for i:=0 to 99 do begin
                a:=round(i+5/10); x:=i/100+5/1000; b:=round(100*x);
                if a<>b then begin Writeln(x:25:25,' -> ',b/100.0:1:5); break; end;
        end;
end.
Вывод:
0.0149999996647238700000000 -> 0.01000
Все операции по прежнему в FPU. И здесь действительно b=1, но a=2 (как и должно быть).

GAA в сообщении #1567466 писал(а):
Нет. Двоичное представление 2.674999999xxx — это и есть для процессора 2.675.
Для процессора 2.675 не представимо. А представимы близкие к нему числа, либо с недостачей, либо с избытком, Вы же сами приводили на первой странице битовое содержимое с недостачей с точным десятичным значением, забыли уже? Да и вот снова привели, 0.135, уже с избытком.

GAA в сообщении #1567475 писал(а):
В комментариях приведены значения после выполнения строки. Желающие на калькуляторе могут проверить.
О, а вот и обещанные глюки в самых неожиданных местах: задание 0.135 как константы (перед присваиванием в переменную) и получение её же операциями FPU дают разные результаты! О чём я и говорил выше. Вот код:
код: [ скачать ] [ спрятать ]
Используется синтаксис Delphi
uses    SysUtils;
var     i,a,b:integer; t,x,y,z:extended; p:pointer;
begin
        t:=0.135;
        for i:=13 to 13 do begin
                x:=i+5/10;
                z:=i/100+5/1000;
                y:=100*z;
                a:=round(x); b:=round(y);
                if a<>b then begin
                        Writeln(z:25:25,' -> ',b/100:5:5);
                        p:=@t; Writeln(IntToHex(int64(p^),16));
                        p:=@x; Writeln(IntToHex(int64(p^),16));
                        p:=@y; Writeln(IntToHex(int64(p^),16));
                        p:=@z; Writeln(IntToHex(int64(p^),16));
                        break;
                end;
        end;
end.
Вывод (только мантиссы):
8A3D70A3D70A3D71 - это точное значение 0.135 как константы
D800000000000000 - это i+5/10 для i=13
D7FFFFFFFFFFFFFF - это 100*(i/100+5/1000) для i=13
8A3D70A3D70A3D70 - это i/100+5/1000 для i=13
Как прекрасно видно вычисленное значение 0.135 и соответственно 100*0.135 не совпадают с идеальными.
Будет другая формула вычисления величины перед округлением (и домножением на $10^k$) — глюки будут на других числах. И зависимость далеко не только от $k$ (номер округляемой цифры). О чём и толкую. Бесполезно искать до какой цифры можно "безопасно" округлять в каком формате, если число не представимо дробью $a/2^t$ - могут быть глюки, на тех или иных числах.

 Профиль  
                  
 
 Re: Оператор round() в python 3
Сообщение24.10.2022, 01:31 
Заслуженный участник


12/07/07
4522
Нет, надо x:=100*(i/100+5/1000); b:=round(x);

-- Mon 24.10.2022 00:35:14 --

Всё остальное, что Вы написали я уже давно знаю. И тему я читал внимательно. И да общий вывод: нужно использовать символьные вычисления или близкие к ним. Но я же не об этом пишу на этой странице.

-- Mon 24.10.2022 01:21:07 --

Увеличил диапазон
Используется синтаксис Delphi
var
 i,a,b:int64; x: Double;
 LH: int64 = 99999999999;

begin
 i:= 0;
 while i <= LH
  do begin
      a:=round(i+5/10); x:=100*(i/100+5/1000); b:=round(x);
      if a<>b then begin Writeln(x:25:25,' -> ',b/100:1:5); break; end;
      inc(i);
     end;
 writeln('Ok');
end.
Завершение с "Ok". При дальнейшем увеличении диапазона уже время вычислений начинает раздражать. Хорошо бы придумать конкретный контрпример.

 Профиль  
                  
 
 Re: Оператор round() в python 3
Сообщение24.10.2022, 02:38 
Заслуженный участник


12/07/07
4522
GAA в сообщении #1567475 писал(а):
x:= 0.135; {3FC147AE147AE148 = 0.1350000000000000088817841970012523233890533447265625}
x:= 100*x; {402B000000000000 = 13.500000000000000000000000000000000000000000000000000}
Вы проверили? Опечатка? Ошибка?

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

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



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

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


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

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