2014 dxdy logo

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

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




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


31/08/22
179
Вы совершенно верно выше писали что "корень зла" в машинном представлении чисел с плавающей точкой посредством мантиссы и экспоненты, фактически это аппроксимация числа и не зависит от ЯП. Это особенности регистров процессора. Ради фана провел тот же эксперимент с округлением на C# и C++ результаты точно такие же. Так что не в питоне дело.
Это же "корень зла" в накоплении ошибок алгоритмов.

GAA в сообщении #1567134 писал(а):
decimal — это символьные вычисления

Разве? Нет, это целочисленные вычисления. Но не за одну процессорную команду.
Весь процессор состоит из целочисленных регистров, целыми числами являются и сами команды. Для работы с плавающей точкой есть сопроцессор (в начале он был отдельной микросхемой, а потом стал частью процессора). Если процессор 32х битный, он может работать с 64х битными числами, условно за 2 команды. Если процессор 64х битный то соответственно за 1 команду.
Так же есть регистры векторизации типа SSE, AVX. 128 и 256 бит соответственно. Таким образом если задействована векторизация можно добиться выполнения операций одновременно над 2мя 64х битными числами. Вот одно число 128 бит там вроде нельзя. Но модно создать алгоритм который использовал бы эти регистры для счета 128 битного числа.
https://stackoverflow.com/questions/12200698/is-it-possible-to-use-sse-and-sse2-to-make-a-128-bit-wide-integer
Примерно так же поступает и Decimal. Каждая операция там целый алгоритм, а не инструкция процессора. Поэтому он в разы медленнее.
Некоторые пояснения, определения и рекомендации
https://learn.microsoft.com/ru-ru/dotnet/api/system.decimal?view=net-6.0&viewFallbackFrom=dotnet-plat-ext-7.0
https://learn.microsoft.com/ru-ru/dotnet/visual-basic/language-reference/data-types/decimal-data-type
https://translated.turbopages.org/proxy_u/en-ru.ru.99c1f335-63502d43-52f62f3a-74722d776562/https/www.codeproject.com/articles/3667/a-decimal-class-implementation
https://tirinox.ru/decimal-vs-float/?ysclid=l9fvmvixvk472722272
https://stackoverflow.com/questions/14096026/c-decimal-data-types
Как видно реализации сделаны на базе целочсленных типов, битность может быть разная. На ЯП не обращайте внимания во всех языках концепция примерно одна.
Помню во времена первых квейков и думов в графицеских движках изголялись создавая числа с фиксированной точкой на базе целочисленных регистров, целые числа считались в разы быстрее чисел с плавающей.
С Decimal схож подход создания чисел с большей битностью чем может позволить регистр процессора. Почти в каждом ЯП можно найти класс BigInteger и т.д. который может быть хоть 1000 бит. В нем так же каждая операция целый алгоритм а не одна команда процессора.

GAA в сообщении #1567134 писал(а):
стало интересно: каково соотношение скоростей выполнения численных вычислений, пусть на базе NumPy, и вычислений с decimal. Есть ссылки на такие тесты или результаты собственных?

Тесты не потребуются если знать следующее.
Все объекты питона создаются в виде структуры содержащей базовую информацию об объектах. Там содержится имя объекта, счетчик ссылок на объект которые постоянно отслеживаются и когда они обнуляется GC (Garbage Collector) его в праве затереть и т.д. Если создать простой нативный Integer то он будет весить, внимание аж 26 байт! (если я верно помню.) Не бит! Так что нативнми переменными можно жрать память тоннами. И при вычислениях есть некоторые издержки.
Но не все так плохо. Модули для питона обычно пишут на C++, там Integer занимает нормальные 32бита и выполняется за одну команду процессора (намеренно пишу команду а не такт, потому что тактов на разные команды разное количество и оно варьируется от модели к модели и вендору). Весь NumPy написан на C++ и тут уже можно ожидать хорошую производительность (и она там есть, тестировал NumPy портированный под C#. Он оказался весьма не плохой библиотекой, но не лучшей.)
А всевозможные модули использующие GPU ускоряют просто фантастически.
Отдельно нужно сказать, что питон изначально интерпретируемый ЯП - т.е. со скорость все плохо изначально.
Так же есть всевозможные декораторы которые интерпретируемый код питона переводят в компилированный и прочие изощрения.
Таким образом код на базе нативных переменных будет выдавать одну скорость, в NumPy будет выдавать фактически скорость C++ с издержками на вызовы и не самую быструю реализацию алгоритмов.

Вывод: Нативный код питона нужно использовать по минимуму. Максимально нужно задействовать модули и всякие бубны ускоряющие программу.

Постарался кратко описать суть дела. Надеюсь у меня это получилось и стало ясно, что сравнивать в принципе бессмысленно, нужно просто использовать с умом.
C# можно сравнивать с Java они оба транслируемые. C++, C, Pascal, Fortran... компилируемые. Питон это что типа Lua - интерпретируемые.

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


12/07/07
4438
Символьные движки основываются [точнее ранее были основаны] на регистрах общего назначения. Но это вопрос о названиях. Пусть целочисленные вычисления.
А интересно следующее. Во сколько раз мы потеряем в скорости выполнения при переходе с арифметики с плавающей точкой на decimal (на том же самом процессоре). Просто указать тип арифметики с плавающей точкой (FPU-ориентированная, или SSE-ориетированная, или AVX-256, или AVX-512, но указать какая именно версия SSE, AVX) ну и код (32-битный или 64-битный).
Понятно, что вариантов масса. Наверное, FPU для плавающей точки и 32-битный код неактуальны, но если есть хоть такие тесты, то уже интересно. (Чтобы представлять сколько потеряем при перехода с double на decimal.) Естественно, модули должны быть оптимизированы, но и сведения о плохо оптимизированных модулях интересны (в цифрах и конкретных недостатках).
А из общих соображений тут прикинуть мне сложно.

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


01/09/13
4318
GAA в сообщении #1567167 писал(а):
Естественно, модули должны быть оптимизированы

Тут есть особенность - например, интел имеет собственные версии лапака, в два-три раза быстрее "стандартных"...
Так что ещё и конкретный процессор надо иметь в виду.

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


12/07/07
4438
Заглянул, Schrodinger's cat, по указанной ссылке
Microsoft на странице Decimal Структура писал(а):
Decimal представляет десятичные числа в диапазоне от плюс 79,228,162,514,264,337,593,543,950,335 до минус 79,228,162,514,264,337,593,543,950,335.
Т.е. в C# это тип с фиксированной длиной (128 бит). И это то, что я привык называть целочисленным типом. (Decimal в C# — это «для финансовых вычислений».) Но Decimal в Python может иметь неограниченную точность. Именно это я подразумевал, когда говорил о символьных вычислениях.
Python 3.6.9:
Используется синтаксис Python
rom decimal import *
getcontext().prec = 100
d = Decimal(Decimal(10000000000000000000000000000000000000000000000000000000000)/Decimal(3))
print(d)
3333333333333333333333333333333333333333333333333333333333.333333333333333333333333333333333333333333
Можно, конечно, и увеличить prec. (Просто в сообщение не хотелось вставлять длинную строку.)

Geen, да, конечно. При указании результатов тестов нужно указывать процессор и выполнялась ли оптимизация под эту модель. Но в целом это изменит результат, в крайнем случае, в разы. И это конечно интересно, но для начала хотелось бы увидеть порядки (в зависимости от величины prec).

-- Thu 20.10.2022 01:24:24 --

К слову. Вроде бы из математических функций модуль Decimal Python поддерживает только sqrt, exp, ln и log10. Остальное нужно писать самому или искать сделанные кем-то модули / функции.

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


12/07/07
4438
Ещё из математических функций есть power и некоторые другие более специфические, но это особо ничего не меняет: нет тригонометрических, обратных тригонометрических,...

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


31/08/22
179

(Оффтоп)

Код:
# Online Python - IDE, Editor, Compiler, Interpreter

import time
import random
from decimal import *

def TestInt(cycles):
    list0=[]
    list1=[]
    list2=[]
    for i in range(cycles):
        list0.append(random.randint(0,101))
        list1.append(random.randint(0,101))
        list2.append(random.randint(0,101))
       
    #print(list0)
       
    start = time.time_ns()
   
    for i in range(cycles):
        list2[i] = list0[i] + list1[i]
   
    end = time.time_ns()
   
    print("TestInt", end - start, "ns")
   
def TestFlt(cycles):
    list0=[]
    list1=[]
    list2=[]
    for i in range(cycles):
        list0.append(random.uniform(0.0,1.0))
        list1.append(random.uniform(0.0,1.0))
        list2.append(random.uniform(0.0,1.0))
       
    #print(list0)
       
    start = time.time_ns()
   
    for i in range(cycles):
        list2[i] = list0[i] + list1[i]
   
    end = time.time_ns()
   
    print("TestFlt", end - start, "ns")
   
def TestDcm(cycles):
    list0=[]
    list1=[]
    list2=[]
    for i in range(cycles):
        list0.append(Decimal(random.randint(0,101)))
        list1.append(Decimal(random.randint(0,101)))
        list2.append(Decimal(random.randint(0,101)))
       
    #print(list0)
       
    start = time.time_ns()
   
    for i in range(cycles):
        list2[i] = list0[i] + list1[i]
   
    end = time.time_ns()
   
    print("TestDcm", end - start, "ns")

cycles = 200000
TestInt(cycles)
TestFlt(cycles)
TestDcm(cycles)


Накидал тест с нативными типами, но онлайн компилятор не позволяет измерить. У меня такое ощущение что моя задача в самом низком приоритете делит время выполнения с еще сотней таких же. Получается рулетка.
Запустите кто нибудь пожалуйста на своем компьютере.
Количество циклов cycles нужно увеличить.

NumPy к сожалению не поддерживает Decimal
https://numpy.org/doc/stable/user/basics.types.html
Но оставляет возможность использовать его как контейнер для любых других типов при этом тип указывается "object". Не знаю будет ли он считать если туда запихивать Decimal при этом.

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


11/06/12
10390
стихия.вздох.мюсли
GAA в сообщении #1567150 писал(а):
На графике плохо видно.
Да, он носит декоративный иллюстративный характер, чисто показать, что
Aritaborian в сообщении #1567138 писал(а):
не всё гладко
Таблица:
Код:
Table[{.05 + i/10, Round[.05 + i/10, .1]}, {i, 0, 50}]//Column
Код:
{
{{0.05, 0.}},
{{0.15, 0.2}},
{{0.25, 0.2}},
{{0.35, 0.3}},
{{0.45, 0.4}},
{{0.55, 0.6}},
{{0.65, 0.6}},
{{0.75, 0.8}},
{{0.85, 0.8}},
{{0.95, 1.}},
{{1.05, 1.}},
{{1.15, 1.2}},
{{1.25, 1.2}},
{{1.35, 1.4}},
{{1.45, 1.4}},
{{1.55, 1.6}},
{{1.65, 1.6}},
{{1.75, 1.8}},
{{1.85, 1.8}},
{{1.95, 2.}},
{{2.05, 2.}},
{{2.15, 2.1}},
{{2.25, 2.2}},
{{2.35, 2.3}},
{{2.45, 2.4}},
{{2.55, 2.5}},
{{2.65, 2.6}},
{{2.75, 2.8}},
{{2.85, 2.8}},
{{2.95, 2.9}},
{{3.05, 3.}},
{{3.15, 3.1}},
{{3.25, 3.2}},
{{3.35, 3.3}},
{{3.45, 3.4}},
{{3.55, 3.5}},
{{3.65, 3.6}},
{{3.75, 3.8}},
{{3.85, 3.8}},
{{3.95, 3.9}},
{{4.05, 4.}},
{{4.15, 4.1}},
{{4.25, 4.2}},
{{4.35, 4.3}},
{{4.45, 4.4}},
{{4.55, 4.5}},
{{4.65, 4.6}},
{{4.75, 4.8}},
{{4.85, 4.8}},
{{4.95, 5.}},
{{5.05, 5.}}
}

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


31/08/22
179

(Оффтоп)

Код:
using System;
using System.Diagnostics;

class HelloWorld
{
    static void TestInt(int cycles)
    {
        var rand = new Random();
        int[] list0 = new int[cycles];
        int[] list1 = new int[cycles];
        int[] list2 = new int[cycles];
       
        for(int i=0; i<cycles; i++)
        {
            list0[i] = rand.Next(0,101);
            list1[i] = rand.Next(0,101);
            list2[i] = rand.Next(0,101);
        }
       
        Stopwatch stopWatch = new Stopwatch();
        stopWatch.Start();
       
        for(int i=0; i<cycles; i++)
            list2[i] = list0[i] + list1[i];
   
        stopWatch.Stop();
        TimeSpan ts = stopWatch.Elapsed;
        Console.WriteLine("TestInt " + ts.TotalMilliseconds);
    }
   
    static void TestFlt(int cycles)
    {
        var rand = new Random();
        float[] list0 = new float[cycles];
        float[] list1 = new float[cycles];
        float[] list2 = new float[cycles];
       
        for(int i=0; i<cycles; i++)
        {
            list0[i] = (float)rand.NextDouble();
            list1[i] = (float)rand.NextDouble();
            list2[i] = (float)rand.NextDouble();
        }
       
        Stopwatch stopWatch = new Stopwatch();
        stopWatch.Start();
       
        for(int i=0; i<cycles; i++)
            list2[i] = list0[i] + list1[i];
   
        stopWatch.Stop();
        TimeSpan ts = stopWatch.Elapsed;
        Console.WriteLine("TestFlt " + ts.TotalMilliseconds);
    }
   
    static void TestDbl(int cycles)
    {
        var rand = new Random();
        double[] list0 = new double[cycles];
        double[] list1 = new double[cycles];
        double[] list2 = new double[cycles];
       
        for(int i=0; i<cycles; i++)
        {
            list0[i] = rand.NextDouble();
            list1[i] = rand.NextDouble();
            list2[i] = rand.NextDouble();
        }
       
        Stopwatch stopWatch = new Stopwatch();
        stopWatch.Start();
       
        for(int i=0; i<cycles; i++)
            list2[i] = list0[i] + list1[i];
   
        stopWatch.Stop();
        TimeSpan ts = stopWatch.Elapsed;
        Console.WriteLine("TestDbl " + ts.TotalMilliseconds);
    }
   
    static void TestDcm(int cycles)
    {
        var rand = new Random();
        decimal[] list0 = new decimal[cycles];
        decimal[] list1 = new decimal[cycles];
        decimal[] list2 = new decimal[cycles];
       
        for(int i=0; i<cycles; i++)
        {
            list0[i] = (Decimal)rand.NextDouble();
            list1[i] = (Decimal)rand.NextDouble();
            list2[i] = (Decimal)rand.NextDouble();
        }
       
        Stopwatch stopWatch = new Stopwatch();
        stopWatch.Start();
       
        for(int i=0; i<cycles; i++)
            list2[i] = list0[i] + list1[i];
   
        stopWatch.Stop();
        TimeSpan ts = stopWatch.Elapsed;
        Console.WriteLine("TestDcm " + ts.TotalMilliseconds);
    }
   
    static void Main()
    {
        int cycles = 10000000;
        TestInt(cycles);
        TestFlt(cycles);
        TestDbl(cycles);
        TestDcm(cycles);
    }
}


Накидал тест в C# онлайн компилятор так же выдает рулетку, но гораздо стабильнее. Можно запустить на компе и получить значения точнее.
Теперь можно сравнить.
Питон 200000 циклов за
TestInt 189877904 ns (189.877904 ms)
TestFlt 140578248 ns (140.578248 ms)
TestDcm 247285410 ns (247.285410 ms)
(Как я писал выше онлайн выдает очень приблизительные цифры, но все равно можно заметить что Decimal тормознее других типов. И врядли decimal питона быстрее C# это скорее всего некорректно выдает онлайн тест)

C# 10000000 циклов за
TestInt 27.0387 ms
TestFlt 30.4232 ms
TestDbl 23.8172 ms
TestDcm 321.4032 ms

Flt - float, Dbl - double, Dcm - decimal.
Для С++ в сравнении с C# может быть примерно так же или немного быстрее. Не проверял но думаю 1-10 раз. C# далеко не всегда проигрывает С++.
В реальной программе питон в 10-10000 раз медленнее C# проверял.
Еще нужно обратить внимание на то, что в питоне decimal задаваемый (я не смотрел его внутреннее устройство, надо полагать наверно на базе int64) в C# это 128бит в С++ на выбор 32, 64, 128.
Вот Вам порядки по скорости.

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


12/07/07
4438
Aritaborian, я синтаксис Mathematica не знаю, но вроде ожидаемо, судя по справке.
Round[x,a] rounds to the nearest multiple of a.
<...>
If a is not a real number, Round[x,a] is given by the formula Round[x,a] =a Round[x/a].
Нужно заменить 0.1 на 1/10. Для первых десяти значений Wolfram Alpha вроде правильно считает
Код:
round({5/100+0/10, 5/100+1/10, 5/100+2/10,5/100+3/10, 5/100+4/10, 5/100+5/10, 5/100+6/10, 5/100+7/10, 5/100+8/10, 5/100+9/10}, 1/10)/1.0
Результат: {0., 0.2, 0.2, 0.4, 0.4, 0.6, 0.6, 0.8, 0.8, 1.}. Т.е. {{0.35, 0.3}} не будет.

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


20/08/14
11067
Россия, Москва
GAA
Более короткая запись.

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


12/07/07
4438
Dmitriy40, спасибо.
И вот если мы в Wolfram Alpha в каком-то месте заменяем дроби на десятичные, то вылазят «глюки»
table[{.05+x/10, round(0.05+x/10, 1/10)/1.0},{x,0,50}]
Код:
(0.05 | 0
0.15 | 0.2
0.25 | 0.2
0.35 | 0.3
0.45 | 0.4
0.55 | 0.6
0.65 | 0.6
0.75 | 0.8
0.85 | 0.8
0.95 | 1
1.05 | 1
1.15 | 1.2
1.25 | 1.2
1.35 | 1.4
1.45 | 1.4
1.55 | 1.6
1.65 | 1.6
1.75 | 1.8
1.85 | 1.8
1.95 | 2
2.05 | 2
2.15 | 2.1
2.25 | 2.2
2.35 | 2.3
2.45 | 2.4
2.55 | 2.5
2.65 | 2.6
2.75 | 2.8
2.85 | 2.8
2.95 | 2.9
3.05 | 3
3.15 | 3.1
3.25 | 3.2
3.35 | 3.3
3.45 | 3.4
3.55 | 3.5
3.65 | 3.6
3.75 | 3.8
3.85 | 3.8
3.95 | 3.9
4.05 | 4
4.15 | 4.1
4.25 | 4.2
4.35 | 4.3
4.45 | 4.4
4.55 | 4.5
4.65 | 4.6
4.75 | 4.8
4.85 | 4.8
4.95 | 5
5.05 | 5)
При этом round(0.35, 1/10)/1.0 или round(0.35, 0.1) вернёт 0.4, но round({0.35}, 1/10)/1.0 вернёт {0.3}.

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


12/07/07
4438
Ну и в случае «списка от округлённых значений» Wolfram Alpha
{round(5/100+2/10, 0.1), round(5/100+3/10, 0.1), round(5/100+4/10, 0.1)} вернёт {0.2, 0.3, 0.4}, хотя {round(5/100+3/10, 0.1)} вернёт 0.4. Т.е. нужно разбираться со списками.

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


11/06/12
10390
стихия.вздох.мюсли
GAA в сообщении #1567203 писал(а):
Нужно заменить 0.1 на 1/10.
Но тогда и 0.05 заменить на 1/20. После этого никаких отклонений не будет.

Изображение

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


12/07/07
4438
Aritaborian в сообщении #1567214 писал(а):
Но тогда и 0.05 заменить на 1/20. После этого никаких отклонений не будет.
Я уже дважды выше в теме это написал
GAA в сообщении #1567150 писал(а):
Я посчитал в Wolfram Alpha
round(10*(5/100+i/10))/10.0 для i = 0..9. <...> Получилось: {0., 0.2, 0.2, 0.4, 0.4, 0.6, 0.6, 0.8, 0.8, 1.}. Тут вроде всё правильно.
GAA в сообщении #1567203 писал(а):
round({5/100+0/10, 5/100+1/10, 5/100+2/10,5/100+3/10, 5/100+4/10, 5/100+5/10, 5/100+6/10, 5/100+7/10, 5/100+8/10, 5/100+9/10}, 1/10)/1.0
Результат: {0., 0.2, 0.2, 0.4, 0.4, 0.6, 0.6, 0.8, 0.8, 1.}. Т.е. {{0.35, 0.3}} не будет.

Но это как бы на случай, если мы не работаем с реальными данными, а искусственно генерируем тест. А реальные данные — десятичные дроби — занесены, как правило, в список. [В случае единичной десятичной дроби все работает вроде правильно.] А вот со списками десятичных дробей затруднения. Но я языка Mathematica не знаю. (Даже не знаю, что из себя представляют десятичные дроби в этом языке.) Насколько такое поведение при работе со списками соответствует языку или это ошибка...

-- Thu 20.10.2022 16:04:45 --

И если такое поведение соответствует языку, то что делать при работе со списками десятичных дробей? Может в цикле по списку идти или ... Понятия не имею.

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


11/06/12
10390
стихия.вздох.мюсли
GAA в сообщении #1567216 писал(а):
Я уже дважды выше в теме это написал
Простите мою невнимательность.
Насчёт списков и циклов я пока не уловил суть проблемы, надо перечитать всё вами написанное, одно могу сказать, что Table это и есть цикл. В языке есть отдельно конструкция For, но она на практике она не нужна почти никогда.

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

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



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

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


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

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