C # Получение странных результатов для простых математических операций

c# math console-application

1726 просмотра

4 ответа

1871 Репутация автора

Я заметил странное поведение при добавлении двух двойных чисел, иногда это работает правильно, а иногда нет!

Вот первый пример:

double num1 = 0.1 + 0.7; //Guess the result?

Легко - 0,8 !!! или нет?

Посмотрите на странный результат:

введите описание изображения здесь

И угадайте, что, если оператор идет внутри блока else, и печатает num1- и нет, он не печатает 0,799999999999993, он печатает 0,8.

Итак, я сделал еще один шаг вперед и попробовал этот код:

if (0.1 + 0.7 == 0.8) //Returns false ??
{
    Console.WriteLine("Correct");
}

Хорошо, странно, но теперь я нашел правильный путь, он должен использовать f (float). Как я помню, в double много пробелов, поэтому он может содержать более высокие числа, возможно, в этом причина.

float num2 = 0.1f + 0.7f;

if (num2 == 0.8f) //Perfect - finally works !!!
{
    Console.WriteLine("Correct");
}
else
{
    Console.WriteLine(num2);
}

Но сейчас я пытаюсь это сделать - и опять возвращается ложь, почему?

if (0.1f + 0.7f == 0.8f) //Returns false :(
{
    Console.WriteLine("Correct");
}

Результаты часов при его отладке:

введите описание изображения здесь

Может кто-нибудь объяснить мне, что здесь не так? Это ошибки?

Заранее спасибо.

Автор: Misha Zaslavsky Источник Размещён: 26.10.2013 12:57

Ответы (4)


6 плюса

47921 Репутация автора

Решение

Арифметика с плавающей точкой не является точной. Вы должны прочитать статью « Что должен знать каждый компьютерщик об арифметике с плавающей точкой» . Если вы хотите, чтобы ваши вычисления были точными в десятичном смысле, вы должны использовать decimalтип, где 0.1m + 0.7m == 0.8mесть true. Следует отметить, что decimalиспользуются числа с плавающей запятой, такие же как floatи double, за исключением того, что основание равно 10, а не 2. Поскольку работа с основанием 2 намного проще и оптимизирована, decimalнамного медленнее. Он также имеет свои собственные неточности, он точен только при работе с числами, которые могут быть представлены коротким числом десятичных цифр (например, 0.7и 0.1), что делает его пригодным для финансовых данных.

Еще одна полезная статья для понимания чисел с плавающей запятой - это формат Википедии с двойной точностью .

Автор: Tim S. Размещён: 26.10.2013 01:05

1 плюс

4010 Репутация автора

внутренне, double хранится в битах, в base-2. Все целые числа могут быть представлены точно в base-2, но когда дело доходит до дроби, это другое дело, все вместе.

Точно так же, как мы не можем точно представить результат 1/3 в нашей десятичной системе, в двоичной системе вы не можете представить все дроби. Поскольку десятичная дробь - это основание-10, легко представить x / 10 в десятичной системе счисления, но в base-2 это невозможно для некоторых x.

Поэтому всякий раз, когда вы используете удвоения, всегда помните о некоторых ошибках округления.

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

Автор: oerkelens Размещён: 26.10.2013 01:08

1 плюс

135 Репутация автора

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

using System;

public class Example
{
   public static void Main()
   {
      double value1 = .333333333333333;
      double value2 = 1.0/3;
      int precision = 7;
      value1 = Math.Round(value1, precision);
      value2 = Math.Round(value2, precision);
      Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2));
   }
}
// The example displays the following output: 
//        0.3333333 = 0.3333333: True
Автор: Janycz Размещён: 26.10.2013 01:09

1 плюс

16283 Репутация автора

Вот ответ: http://www.codeproject.com/Articles/383871/Demystify-Csharp-floating-point-equality-and-relat

Плавающие значения являются только приблизительными. Чтобы сравнить два значения с плавающей запятой, необходимо проверить разницу между ними. Разница не может быть больше чемfloat.Epsilon

Используйте это, чтобы сравнить эти значения:

bool AlmostEqualDoubles(double nVal1, double nVal2, int nPrecision = 6)
{
   nPrecision = Math.Max(Math.Min(16, nPrecision), 0);
   double nEpsilon = 1.0;
   for (int i = 0; i < nPrecision; i++) nEpsilon *= 0.1;       
   return (nVal2 - nEpsilon) < nVal1 && nVal1 < (nVal2 + nEpsilon);
}
Автор: Damian Drygiel Размещён: 26.10.2013 01:13
Вопросы из категории :
32x32