2014 dxdy logo

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

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




Начать новую тему Ответить на тему На страницу 1, 2, 3, 4  След.
 
 Оператор round() в python 3
Сообщение17.10.2022, 14:46 


17/02/15
78
Может кто-либо внятно объяснить алгоритм работы этого оператора? Округление до целых дает ближайшее четное, округление до десятых, сотых и далее дает непредсказуемые результаты. Общие слова по этому вопросу имеются. Какова предсказуемость работы данного оператора?

На настоящий момент я осознаю, что работает банковское округление на которое накладывается погрешность при работе с числами с плавающей точкой.

Вывод. Предсказательной силы нет.

Это истинный вывод или ложный? Есть еще какие-либо детали в данном вопросе?

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


02/08/11
7003
A.M.V. в сообщении #1566927 писал(а):
Округление до целых дает ближайшее четное, округление до десятых, сотых и далее дает непредсказуемые результаты.
Например?

 Профиль  
                  
 
 Re: Оператор round() в python 3
Сообщение17.10.2022, 16:20 
Аватара пользователя


11/06/12
10390
стихия.вздох.мюсли
A.M.V., приведите, пожалуйста, конкретные примеры неправильной с вашей точки зрения работы оператора с указанием версии Питона, а также вашего мнения, какой результат должен быть верным в том или ином случае.

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


20/08/14
11776
Россия, Москва
Python 3.6.9:
Используется синтаксис Python
for i in range(0,30):
 x=0.05+i/10; print(x,"->",round(x,1))
код: [ скачать ] [ спрятать ]
Используется синтаксис Text
0.05 -> 0.1
0.15000000000000002 -> 0.2
0.25 -> 0.2
0.35 -> 0.3
0.45 -> 0.5
0.55 -> 0.6
0.65 -> 0.7
0.75 -> 0.8
0.8500000000000001 -> 0.9
0.9500000000000001 -> 1.0
1.05 -> 1.1
1.1500000000000001 -> 1.2
1.25 -> 1.2
1.35 -> 1.4
1.4500000000000002 -> 1.5
1.55 -> 1.6
1.65 -> 1.6
1.75 -> 1.8
1.85 -> 1.9
1.9500000000000002 -> 2.0
2.05 -> 2.0
2.1500000000000004 -> 2.2
2.25 -> 2.2
2.35 -> 2.4
2.45 -> 2.5
2.55 -> 2.5
2.6500000000000004 -> 2.7
2.75 -> 2.8
2.85 -> 2.9
2.95 -> 3.0
3.05 -> 3.0
3.1500000000000004 -> 3.2
3.25 -> 3.2
3.3499999999999996 -> 3.3
3.45 -> 3.5
3.55 -> 3.5
3.6500000000000004 -> 3.7
3.75 -> 3.8
3.8499999999999996 -> 3.8
3.95 -> 4.0
Ошибочными считаю: 0.05, 0.35, 0.45, 0.65, 0.85, 1.15, 1.45, 1.85, 2.45, 2.55, 2.65, 2.85, 3.35, 3.45, 3.55, 3.65 - все округлены не до чётной цифры после запятой. Причём округление очевидно зависит от величины целой части, хотя вообще то не должно.
Очевидно это из-за конечной точности представления чисел float, после умножения на 10 внутри round{x,1) дробная часть не всегда становится ровно 0.5, даже если она и показывается как 0.5, и правило округление к чётной цифре не применяется.

 Профиль  
                  
 
 Re: Оператор round() в python 3
Сообщение17.10.2022, 20:35 
Экс-модератор
Аватара пользователя


23/12/05
12064
Dmitriy40 в сообщении #1566955 писал(а):
все округлены не до чётной цифры после запятой
а почему должно быть до четной?

Upd: Понял, вы имели в виду конкретно для цифры 5.

Dmitriy40 в сообщении #1566955 писал(а):
Причём округление очевидно зависит от величины целой части, хотя вообще то не должно.
Округление зависит от представления числа с плавающей точкой.

Например, число $2.675$ хранится как примерно $2.67499999999999982236431605997495353221893310546875$, поэтому округление до двух знаков даст $2.67$, а не $2.68$, как должно бы быть по правилам округления

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


20/08/14
11776
Россия, Москва
photon
Последним предложением я именно про это и сказал.

Вопрос скорее что с этим делать. И нужно ли вообще что-то делать — вряд ли round() в питоне используется для научных или экономических расчётов, а в остальных случаях на небольшую неточность можно и плюнуть. Если плюнуть нельзя (что необходимо обосновать), то есть много разных методов более точных вычислений, начиная от масштабированной фиксированной точки (удобна для экономики/бухгалтерии), кончая своими типами float или управлением погрешностью вычислений (чаще применяется в научных расчётах).

-- 17.10.2022, 22:08 --

photon в сообщении #1566962 писал(а):
Например, число $2.675$ хранится как примерно $2.67499999999999982236431605997495353221893310546875$,
Про питон не знаю, говорить не буду, но вот про single и double это не так:
Используется синтаксис Delphi
var     x: single;
        y: double;
begin
        x:=2.675; y:=2.675;
        Writeln(x:20:20);
        Writeln(y:20:20);
end.
Выдача:
Используется синтаксис Text
2.67499995231628418000
2.67499999999999982000
Правые три нуля очевидно некорректны, а цифры начиная с 5 в первой строке недостоверны, просто шум (не случайный, а из-за формата хранения).
Можно конечно и прямо по битам всё расписать и проверить, но лень, дельфи5 я верю, да и могу посмотреть асм код:
Используется синтаксис ASM
mov          d,[0004064D0],0402B3333
mov          d,[0004064D4],066666666
mov          d,[0004064D8],040056666
fld          d,[0004064D0]
fld          q,[0004064D4]
Т.е. single значение равно 0x402B3333, double значение равно 0x4005666666666666. Кто хочет воспользуйтесь любыми калькуляторами (например для single и double) для преобразования в более точное десятичное значение обоих значений, на мой взгляд выдача дельфи правильная (с учётом округления).

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


12/07/07
4522
На первый взгляд, проблема в представлении десятичных чисел в двоичном формате.
Надо смотреть исходный текст round, но близкое поведение имеются и в других пакетах, оперирующих с числами с плавающей точкой.
В системах компьютерной алгебры такой проблемы вроде бы нет.
В указанном выше Dmitriy40 Pyton недоступен SymPy.
Maple 7
Код:
> seq(evalf[1](0.05+i/10), i=1..9);
         .2, .2, .4, .4, .6, .6, .8, .8, 1.
В более сложных случаях нужно ручками что-то сделать, но вроде преодолимо.

Немного криво с ходу набрал в Matlab 2013
Код:
i = 0:9;
x =  sym(0.05 + i/10);
vpa(round(vpa(10*x))/10)
ans = [ 0, 0.2, 0.2, 0.4, 0.4, 0.6, 0.6, 0.8, 0.8, 1.0]
Т.е., на первый взгляд, если нужно точное представление десятичных чисел, то, увы, символьные вычисления. А round с плавающей точкой — это для численных расчётов.

Dmitriy40 в сообщении #1566981 писал(а):
но вот про single и double это не так
Вроде photon привел правильное значение.
Если используется одинарная точность IEEE 754 (или более поздний преемственный стандарт), то число 2.675 будет иметь двоичное представление 10.1010110011001100110011. Мантисса числа (IEEE 754) будет содержать 01010110011001100110011 (23 бита). Обратное преобразование в десятичное даёт 2.6749999523162841796875.
Dmitriy40 в сообщении #1566981 писал(а):
Правые три нуля очевидно некорректны, а цифры начиная с 5 в первой строке недостоверны, просто шум (не случайный, а из-за формата хранения).
Просто округление и добавлены нули для получения строки с заданным числом символов.

Если используется двойная точность, то число 2.675 будет иметь двоичное представление
10.101011001100110011001100110011001100110011001100110.
Мантисса числа (IEEE 754) будет содержать 0101011001100110011001100110011001100110011001100110 (52 бита).
Обратное преобразование в десятичное даёт 2.67499999999999982236431605997495353221893310546875.
Это совпадает с числом, приведенным photon.

Ожидается, конечно, что без указания типа Pyton будет работать с double.

Dmitriy40 в сообщении #1566981 писал(а):
double значение равно 0x4005666666666666. Кто хочет воспользуйтесь любыми калькуляторами
Воспользовался встроенным калькулятором Windows для преобразования 0x4005666666666666 в двоичное представление и получил
100000000000101011001100110011001100110011001100110011001100110.
Первые 52 бита этого числа (52 бита мантиссы, биты считаются справа: нулевой бит самый правый)
0101011001100110011001100110011001100110011001100110.
Это совпадает с приведенным выше значением мантиссы.
Функция Writeln Delphy просто не выдаёт все знаки (просто округление и добавление нулей для получения строки с заданным числом символов).

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


20/08/14
11776
Россия, Москва
GAA
Согласен, для double число именно таково, тут я был не прав.
Для single указанное Вами значение тоже конечно точное.

И проблема в непредставимости точно величины $5/10^n$ для $n>1$ ни в single, ни в double, ни в любом другом формате с плавающей точкой. Соответственно для округления не до целых правило округления до чётной цифры не применяется. А вот для $n<2$, в том числе и $n<0$ (т.е. до цифр слева от десятичного разделителя), применять round(x,n) вполне можно, во всяком случае для целых чисел до $2^{24}\approx10^7$ (или $2^{54}\approx10^{16}$), точнее для чисел имеющих столько значащих цифр, т.е. представимых в single/double точно.

GAA в сообщении #1567038 писал(а):
Т.е., на первый взгляд, если нужно точное представление десятичных чисел, то, увы, символьные вычисления. А round с плавающей точкой — это для численных расчётов.
Не обязательно, зависит от задачи, есть много других методов сохранения или управления точностью. Например работать только с рациональными числами, точность это никак не ограничивает (всё равно иррациональные числа непредставимы в компьютерных форматах и всегда округляются до рациональных). Или использовать смещённую фиксированную точку и работать с целыми. Или написать свою round() с любым желаемым округлением. Или математически оценить вносимую погрешность и учесть её в результатах как систематическую (конечно если она не приводит например к расходимости рядов). Или не пользоваться round(), а пользоваться ceil()/floor() (в каждом случае обоснованно выбирая какой), которые работают более точно (но тоже не математически точно). В общем вариантов масса. А универсального как обычно нет.

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


12/07/07
4522
Да, в Python есть рациональные. Спасибо! В приведенном выше Вами в ветке Python 3.6.9
Используется синтаксис Python
from fractions import Fraction
x0 = Fraction(5, 100)
for i in range(0,10):
 x=x0 + Fraction(i, 10); print(float(x),"->",round(float(x*10))/10)
Используется синтаксис Text
0.05 -> 0.0
0.15 -> 0.2
0.25 -> 0.2
0.35 -> 0.4
0.45 -> 0.4
0.55 -> 0.6
0.65 -> 0.6
0.75 -> 0.8
0.85 -> 0.8
0.95 -> 1.0

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


12/07/07
4522
В Python есть ещё модуль decimal. Python 3.6.9:
Используется синтаксис Python
from decimal import *
getcontext().prec = 4
x0 = Decimal(5/100);
for i in range(0,30):
 x=x0+Decimal(i/10); print(x,"->",round(x,1))
код: [ скачать ] [ спрятать ]
Используется синтаксис Text
0.05000 -> 0.0
0.1500 -> 0.2
0.2500 -> 0.2
0.3500 -> 0.4
0.4500 -> 0.4
0.5500 -> 0.6
0.6500 -> 0.6
0.7500 -> 0.8
0.8500 -> 0.8
0.9500 -> 1.0
1.050 -> 1.0
1.150 -> 1.2
1.250 -> 1.2
1.350 -> 1.4
1.450 -> 1.4
1.550 -> 1.6
1.650 -> 1.6
1.750 -> 1.8
1.850 -> 1.8
1.950 -> 2.0
2.050 -> 2.0
2.150 -> 2.2
2.250 -> 2.2
2.350 -> 2.4
2.450 -> 2.4
2.550 -> 2.6
2.650 -> 2.6
2.750 -> 2.8
2.850 -> 2.8
2.950 -> 3.0

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


20/08/14
11776
Россия, Москва
Да, decimal хорошее решение. Когда не нужна высокая скорость расчётов (вроде он обычно не поддерживается аппаратно).

 Профиль  
                  
 
 Re: Оператор round() в python 3
Сообщение19.10.2022, 11:40 


31/08/22
183
Здравствуйте.
https://docs.python.org/3/library/functions.html#round
Что Вы собственно и выяснили экспериментально.

ПС: Нативный питон это не про скорость расчетов. Можно не заморачиваться какой тип, все равно будет медленно. Если для расчетов используется какой то модуль, например NumPy, тогда да можно говорить о какой то скорости. Модули обычно пишутся на других ЯП, в основном на С++.
И памяти нативные типы жрут как не в коня.

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


12/07/07
4522
Вот откуда магическая цифра 2.675 взялась!

Проблемы с округлением, как я писал выше, имеются в различных инженерных пакетах. Например, в Matlab R2013 в справке к численному round написано
Цитата:
Y = round(X) rounds the elements of X to the nearest integers. Positive elements with a fractional part of 0.5 round up to the nearest positive integer. Negative elements with a fractional part of -0.5 round down to the nearest negative integer. For complex X, the imaginary and real parts are rounded independently.
В справке к символьному округлению:
Цитата:
Y = round(X) rounds the elements of X to the nearest integers. Values halfway between two integers are rounded away from zero.
Т.е. не имени Гаусса округление. Аналогично и в справке к текущей версии, но в R2022b есть и опция выбора типа округления (я не следил, когда её ввели):
Цитата:
Y = round(X) rounds each element of X to the nearest integer. In the case of a tie, where an element has a fractional part of 0.5 (within roundoff error) in decimal, the round function rounds away from zero to the nearest integer with larger magnitude.
<…>
Y = round(___,TieBreaker=direction) rounds ties as specified by direction. Use this argument after any of the input argument combinations in the previous syntaxes.
<…>
X = 1×6
-2.5000 -1.5000 -0.5000 0.5000 1.5000 2.5000
Yeven = round(X,TieBreaker="even")
Yeven = 1×6
-2 -2 0 0 2 2
И при численном округлении мы это наблюдаем:
Используется синтаксис Matlab M
>> round(1/2)
ans = 1
>> round(5/2)
ans = 3
Однако при символьных вычислениях с vpa имеем округление Гаусса:
Используется синтаксис Matlab M
 >> round(vpa(sym(1/2)))
ans = 0
>> round(vpa(sym(5/2)))
ans = 2
В Octave в связи с особенностью символьной функции округления пользователя предупреждают:
Цитата:
Note as of SymPy 1.5, this function rounds toward even:
round ([sym(5)/2 sym(7)/2])
(sym) [2 4] (1×2 matrix)

В случае 2.675 в Matlab оба вида округления до сотых должны дать 2.68.
Код:
>> format long
>> d = 2.675
d = 2.675000000000000
>> round(100*d)/100
ans = 2.680000000000000
Т.е. что видим, то и получаем. Однако, можно проверить, что в переменных при численном округлении на самом деле лежит. Выполним сценарий
Используется синтаксис Matlab M
d = 2.675;
fid = fopen('d.dat', 'w');
fwrite(fid, d, 'float64');
z = round(d*100)/100;
fwrite(fid, z, 'float64');
fclose(fid);
В файле d.dat будет:
Код:
66 66 66 66 66 66 05 40
71 3D 0A D7 A3 70 05 40
Это
2.67499999999999982236431605997495353221893310546875
и
2.680000000000000159872115546022541821002960205078125
соответственно.

Вот с Matlab эксперименты понадобились. А в Python 3 по данному поводу в справку лень заглядывать. Для меня символьные вычисления в широком смысле это не численные (т.е. на базе с плавающей точкой). Т.е. вычисления с fraction или decimal — это символьные вычисления. И символьные вычисления всегда медленнее численных. Я с Python не знаком, просто в тему заглянул, но стало интересно: каково соотношение скоростей выполнения численных вычислений, пусть на базе NumPy, и вычислений с decimal. Есть ссылки на такие тесты или результаты собственных?

 Профиль  
                  
 
 Re: Оператор round() в python 3
Сообщение19.10.2022, 18:31 
Аватара пользователя


11/06/12
10390
стихия.вздох.мюсли
Провёл свой эксперимент, с Mathematica и её функцией [img]Round[/img]. Как видим, тоже не всё гладко.
Код:
DiscretePlot[Round[.05 + i/10, .1], {i, 0, 50}]

Изображение

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


12/07/07
4522
На графике плохо видно. Я посчитал в Wolfram Alpha round(10*(5/100+i/10))/10.0 для i = 0..9. Точнее
Код:
round({10*(5/100+0/10), 10*(5/100+1/10), 10*(5/100+2/10), 10*(5/100+3/10),10*(5/100+4/10), 10*(5/100+5/10),  10*(5/100+6/10),  10*(5/100+7/10),  10*(5/100+8/10),  10*(5/100+9/10)})/10.0
Получилось: {0., 0.2, 0.2, 0.4, 0.4, 0.6, 0.6, 0.8, 0.8, 1.}.
Тут вроде всё правильно. Для каких i в Mathematica неправильные результаты?

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

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



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

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


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

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