Математика C # дает неправильные результаты! - PullRequest
2 голосов
/ 03 марта 2011

Я понимаю принцип, лежащий в основе этой проблемы, но мне больно думать, что это происходит во всем приложении, и мне нужно найти решение.

double Value = 141.1;
double Discount = 25.0;
double disc = Value * Discount / 100; // disc = 35.275
Value -= disc; // Value = 105.824999999999999

Value = Functions.Round(Value, 2); // Value = 105.82

Я использую double для представления довольно маленьких чисел. Каким-то образом в расчете 141.1 - 35.275 двоичное представление результата дает число, которое составляет всего лишь 0,0000000000001. К сожалению, так как я тогда округляю это число, это дает неправильный ответ.

Я читал об использовании десятичных чисел вместо двойных, но я не могу заменить каждый экземпляр двойного числа десятичным. Есть ли какой-нибудь более простой способ обойти это?

Ответы [ 4 ]

15 голосов
/ 03 марта 2011

Если вы ищете точное представление значений, которые являются естественно десятичными, вам нужно будет заменить double на decimal везде. Вы просто используете неправильный тип данных. Если бы вы использовали short везде для целых чисел, а затем выяснили, что вам нужно справиться с большими значениями, чем это поддерживает, что бы вы сделали? Это то же самое дело.

Однако вы должны действительно попытаться понять, с чего начать ... например, Значение не равно точно 141.1, например.

У меня есть две статьи на эту тему:

3 голосов
/ 03 марта 2011

Вы должны использовать decimal - вот для чего.

Поведение арифметики с плавающей запятой? Это именно то, что он делает. Он имеет ограниченную конечную точность. Не все числа точно представимы. На самом деле существует бесконечное число вещественных чисел, и только конечное число может быть представимым. Ключом к decimal для этого приложения является то, что оно использует представление базы 10 - double использует базу 2.

0 голосов
/ 03 марта 2011

Потратив большую часть утра, пытаясь заменить каждый экземпляр «двойной» на «десятичный» и осознав, что я сражаюсь в проигранной битве, я еще раз взглянул на свою функцию Раунда. Это может быть полезно тем, кто не может реализовать правильное решение:

public static double Round(double dbl, int decimals) {            
        return (double)Math.Round((decimal)dbl, decimals, MidpointRounding.AwayFromZero);
    }

Если сначала привести значение к десятичному знаку, а затем вызвать Math.Round, это вернет «правильное» значение.

0 голосов
/ 03 марта 2011

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

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

...