Deutsch

Округления с операторами арифметических действий и присваивания

1162  
alex445 коренной житель02.01.24 12:28
NEW 02.01.24 12:28 
Последний раз изменено 02.01.24 12:28 (alex445)

Хочу не просто x += 1.22000001 писать, а с округлением. Типа такого

double x = 1.5;

x += 1.22000001;

но чтобы иксу присвоилось уже округлённое значение, чтобы избавиться от погрешностей при работе с double.


Если вызвать Math.Round справа

x += Math.Round(1.22000001, 2)

то получим просто округлённое значение 1.22000001, а потом будет выполнена операция присвоения с возможной ошибкой вычислений.


Насколько я понял, с Math.Round такие операторы использовать нельзя, и надо писать полную запись

x = Math.Round(x + 1.22000001, 2);


Или всё же есть способ?

#1 
Срыв покровов патриот02.01.24 13:38
02.01.24 13:38 
в ответ alex445 02.01.24 12:28, Последний раз изменено 02.01.24 13:39 (Срыв покровов)

опохмеляйтесь


#2 
alex445 коренной житель02.01.24 15:34
NEW 02.01.24 15:34 
в ответ Срыв покровов 02.01.24 13:38

У вас просто случайно так получилось. Результат зависит не только от того, что непосредственно в Round придёт в виде парамера, но и от х. Параметр вы округлили, а х - нет.


Там погрешность округления в последнем разряде при переводе из двоичной системы в десятичную. Поэтому надо периодически округлять до нужной точности. У меня точность с запасом (три знака после запятой, и сами числа не больше 6-7 знаков), и в принципе можно округлять лишь при выводе, при строковом форматировании. Но всё равно не нравится периодически выхватывать эти девятки и единички. Кроме того, при определённом MidpointRounding всякие х,9999999 и х,00000001 легко превращаются не в то, что нужно.

#3 
Срыв покровов патриот02.01.24 15:43
NEW 02.01.24 15:43 
в ответ alex445 02.01.24 15:34

ну так ты и объяснял русским языком, в чем твоя проблема
для таких вещей был придуман тип Decimal


Кроме того, при определённом MidpointRounding всякие х,9999999 и х,00000001 легко превращаются не в то, что нужно.

это ты его используешь не по назначению

#4 
alex445 коренной житель02.01.24 16:01
NEW 02.01.24 16:01 
в ответ Срыв покровов 02.01.24 15:43

Decimal много весит. Я бы и дабл и на флоат заменил, но в Дотнете многие встроенные функции по умолчанию принимают либо инты, либо даблы, и полученные флоаты конвертят в даблы.

#5 
AlexNek патриот02.01.24 18:48
AlexNek
NEW 02.01.24 18:48 
в ответ alex445 02.01.24 16:01, Последний раз изменено 02.01.24 20:20 (AlexNek)
Decimal много весит

Тест проект есть, на сколько мегабайт разница? спок


https://stackoverflow.com/questions/803225/when-should-i-u...

#6 
Бесконечный цикл постоялец02.01.24 19:53
NEW 02.01.24 19:53 
в ответ alex445 02.01.24 12:28

Как-то плохо ты год начал, какой-то поток сознания. Я же говорил, сбегай в дежурную аптеку.


Короткий ответ: поскольку проблемы нет, то ты можешь применять операции в любом порядке и использовать любые типы данных и любой способ округления. Можешь ввести операцию сложения как 2+2=5. А можешь конвертировать в bool а потом xor сверху.


Или может лучше все-таки в Амермку махнуть? Там дома большие, медицина топ, социальная гармония, волны, ветер, телки, дурь бесплатно от государства. Действительно никак?
#7 
AlexNek патриот02.01.24 20:26
AlexNek
NEW 02.01.24 20:26 
в ответ alex445 02.01.24 12:28

А что мешает пользовать public static double operator +(double left, double right)?

https://learn.microsoft.com/en-us/dotnet/csharp/language-r...


#8 
alex445 коренной житель02.01.24 22:16
NEW 02.01.24 22:16 
в ответ AlexNek 02.01.24 20:26, Последний раз изменено 02.01.24 22:28 (alex445)

А смысл операторы перегружать? Там же ошибка возникает не просто в вычислениях, а и в хранении. Данные хранятся в битах, а многие десятичные числа с плавающией заяптой в битах точно не представимы - приходится округлять. Сколько ни приводи типы, ни перегружай операторы - этот "дребезг" последнего разряда by design.


Вобщем, я пишу как и сказал в начале x = Math.Round(x + 1.22000001, 2); Просто хотел, чтобы было коротко и красиво.


Данных примерно до несколько десятков мегабайт может дойти. Вроде немного, даже если удвоить. И даже для мобильных телефонов должно хватить. Но меня смущает, что сериализатор все эти типы представляет как float.


Тут вообще советуют в строках хранить при сериализации

https://stackoverflow.com/questions/35709595/why-would-you...

#9 
AlexNek патриот02.01.24 22:49
AlexNek
NEW 02.01.24 22:49 
в ответ alex445 02.01.24 22:16
А смысл операторы перегружать?

"Результат зависит не только от того, что непосредственно в Round придёт в виде парамера, но и от х. Параметр вы округлили, а х - нет."


Для начала неясна проблема в принципе. смущ Если нужна большая точность, то Decimal

иначе проблемы будут

http://blog.ygrenier.com/2017/10/dotnet-differences-floati...

https://discussions.unity.com/t/c-floating-point-confusion...


https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg...

#10 
alex445 коренной житель03.01.24 11:11
NEW 03.01.24 11:11 
в ответ AlexNek 02.01.24 22:49

"Проблема" - сделать красиво и коротко. Сделать точно я знаю как. ))

#11 
AlexNek патриот03.01.24 20:27
AlexNek
NEW 03.01.24 20:27 
в ответ alex445 03.01.24 11:11
Сделать точно я знаю как.

ну пишите как, будем думать как сделать красиво

#12 
Программист коренной житель09.01.24 08:35
NEW 09.01.24 08:35 
в ответ alex445 02.01.24 12:28

Сделай обертку над даблом.

#13 
Grossmutters_G прохожий09.01.24 18:40
NEW 09.01.24 18:40 
в ответ alex445 02.01.24 12:28, Последний раз изменено 09.01.24 19:38 (Grossmutters_G)

Есть алгоритм Кэхэна для суммы ряда (https://en.wikipedia.org/wiki/Kahan_summation_algorithm), сохраняющий более высокую точность при работе с числами с плавающей запятой.

В англ. варианте статьи описан более корректный алгоритм Ноймаера.

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

Round надо будет применять уже к результату суммирования.


Если же надо прям "абсолютную" точность, то надо использовать, как уже сказали, decimal или методы нормализации (чтобы не работать с машинным представлением вещественных чисел, так как потеря точности там уже на уровне представления появляется. Она хорошая, погрешность, но все равно есть)

#14 
7495 старожил22.01.24 10:45
7495
NEW 22.01.24 10:45 
в ответ alex445 02.01.24 12:28

Округления с операторами арифметических действий и присваивания


Runden zu Integer. In der Welt der Pixel sind ganze Zahlen gefragt.


let x = 17.4999;

let y = Math.round(x); // Auf- oder Abgerundet auf 17


Rundet auf ab .5, rundet ab bis .49999


Runden auf Nachkommastellen. Für das Runden auf 2 Nachkommastellen braucht Javascript einen Trick:


Math.round (217.4325 * 100) / 100; // 217.43


Einfacher geht das Runden in Javascript mit toFixed:


let num = 9.7433789;

let n = num.toFixed(2);


Noch mal zurück auf Runden mit Math. Schluss mit 97 oder 49 Cent: Aufrunden zu 5 oder 0 in der zweiten Kommastelle


let cents = 1017.721987;

let fünfcents = (Math.ceil(cents*20)/20).toFixed(2);

console.log ("fünfcents " + fünfcents)

fünfcents 1017.75

Вопросы и Ответы - Программируем калькулятор пособий для беженцев вместе.
#15 
alex445 коренной житель30.01.24 11:43
NEW 30.01.24 11:43 
в ответ 7495 22.01.24 10:45, Последний раз изменено 30.01.24 11:49 (alex445)

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


Не знаю, как сейчас это требуют решать, а в моё время уже в задании писали (подсказка) - "раскройте скобки и упростите". Т.е. уже подсказал, что сначала нужно раскрыть скобки. Ну и вспомнить (уже совсем большая подсказка), чем смешанная дробь отличается от простой дроби со множителем.

#16