2014-08-27 4 views
1

У меня проблемы с проблемой округления в C#. Я хотел бы округлить результат вычисления до 4 десятичных знаков (awayfromzero). Если я использую Math.Round (переменная, ...), он округляется вниз, если я введу результат вручную, он округляется. Я понятия не имею, почему ..Ошибка округления в C#?

Что я делаю неправильно? Результат ниже код: Округлые: 591,24575 591,2457 - 591,2458

double number1 = 1136.81; 
double number2 = 4.00; 
double number3 = 2182.257; 
double result = (number1 * number2 - number3)/4; 
Console.WriteLine("Rounded: " +result+" " + Math.Round(result, 4, MidpointRounding.AwayFromZero) + " - " + Math.Round(591.24575, 4, MidpointRounding.AwayFromZero)); 
Console.ReadLine(); 
+4

потому что 'double' - это двоичный тип данных с плавающей точкой, который не является 100% точным при преобразовании в/из чисел base-10 (с использованием' Round', 'Floor' и т. Д.). Используйте 'decimal', если вам нужна точность base-10. –

+3

Всегда завораживает меня, что предположение о том, что в каркасе или в C# есть ошибка, когда что-то не работает, как кто-то ожидает. –

+1

@ DanielKelley Чтобы быть справедливым, пользователь действительно сказал: «Что я * делаю неправильно?» – LarsTech

ответ

3

result не совсем 591.24575, это какое-то число, которое очень близко к 591.24575, но это лишь немного меньше, что-то вдоль линий 591.24574999999999, в результате того, что определенные числа, которые имеют конечное число цифр в базе 10, не могут быть представлены с конечным числом цифр в базе 2. Когда вы устанавливаете число точно, вы избегаете когда-либо иметь double, установленный на один этих значений как промежуточное значение ваших вычислений.

Если вы имеете дело с цифрами, имеющими известное фиксированное число базовых 10 цифр, и важно, чтобы у вас не было таких ошибок точности, как это может быть целесообразно использовать в этом контексте Decimal.

+0

Ах, избитый на полминуты :) – Stijn

+0

Кроме того, переходя к чему-то, что вы могли бы сделать, чтобы помочь с такой проблемой, поставили точку останова в вашем коде сразу после назначения результата , Отлаживайте результат приложения и мыши, чтобы увидеть его фактическое значение – Kritner

2

result не имеет значения, которое, по вашему мнению, оно делает, оно усекается, помещая его в строковое представление. А почему именно, читайте What Every Computer Scientist Should Read About Floating Point

Посмотрите во время отладки:

result: 591.24574999999993 
result.ToString(): "591,24575" 
4

Если вы используете мой DoubleConverter, вы можете увидеть точное значение result:

Console.WriteLine(DoubleConverter.ToExactString(result)); 

Это отпечатки:

591.2457499999999299689079634845256805419921875 

..., который округляется до 591.2457 при округлении до 4 знаков после запятой. Когда вы просто печатаете result, он печатает его уже округленным до определенного количества знаков после запятой, что может повлиять на результат, если вы затем (мысленно) округлены до 4 DP.

Вы можете видеть это без каких-либо «нормальных» странностей двоичной с плавающей запятой. Рассмотрим этот код:

decimal exact = 1.2345m; 
decimal rounded2 = Math.Round(exact, 2, MidpointRounding.AwayFromZero); 
decimal rounded3 = Math.Round(exact, 3, MidpointRounding.AwayFromZero); 
decimal rounded3Then2 = Math.Round(rounded3, 2, MidpointRounding.AwayFromZero); 
Console.WriteLine(rounded2); // 1.23 
Console.WriteLine(rounded3); // 1.235 
Console.WriteLine(rounded3Then2); // 1.24 

В своем коде вы не были на самом деле выполнения «два закругления» - но вы мысленно сделать это, принимая печатное значение result (591.24575) и предполагая, что вы могли бы принять, что быть с тем чтобы округлить его дальше.

Смежные вопросы