0
            double dTest1 = Convert.ToDouble("1005,502247");
            double dTest2 = Convert.ToDouble("1005,142247");
            double dTest3 ;
            dTest3 = (dTest1 - dTest2);

На выходе в отладчике dTest3 0.36000000000001364. Откуда хвост, что это, как исправлять?

cProject
  • 167

1 Answers1

7

Это проблема проявляется не только с C#, но и в C++, js, python, java etc - везде, где есть тип числа с плавающей точкой, и связана с тем, как число с плавающей точкой вычисляется и хранится в памяти компьютера. И касается она не только double, но и float.

Если вдаться в подробности, то число с плавающей точкой типа double(float) хранится в памяти в виде мантисы и экспоненты в двоичном виде. При чем длина мантисы ограничена 23 битами для float, и 52 - для double, а экспоненты - 8-ю и 11-ю соответственно. В этом случае значение переменной вычисляется по формуле: (-1)^sign* (1.m) * (2 ^ e). Такой подход обеспечивает точность не более 7-ми знаков после запятой для float и 15 - для double.

Как происходит запись?

Возьмём пример числа:

double d = 12;

12 = (8+4)=(1+1/2)*2^3. - здесь экспонента будет равна 100 (3 в бинарном виде), а мантисса - 1 (поскольку 1/2 - в бинарном виде 0.1).. - это простой пример, и он вполне может храниться в памяти без потери. От простого к сложному:

2.1 = (2 + 0.1)=(1+0.05)*2

Тут уже e=1 А вот m=?

Здесь ждёт сюрприз:

Можете проверить меня, переведя 0.05 из десятичной в двоичную систему. На моём калькуляторе результат выглядит так:

m = 00001100110011001100110011001100110011001100110011

Очевидно, что при хранении этого числа будет потеряна точность.

Что можно почитать на тему:

Решение:

Для решения сложившейся ситуации есть несколько путей:

  1. Решение в лоб: округлить результат выполнения операций над double/float с помощью Math.Round:

    var r = Math.Round(d, 2);

  2. Использовать специальный тип для хранения чисел с плавающей точкой под названием Decimal. Это надёжное и впрочем рекомендуемое решение, но нужно учесть, что при больших вычислениях может быть незначитедьная потеря в производительности

Александр
  • 1,978
  • 1
  • 16
  • 25