double dTest1 = Convert.ToDouble("1005,502247");
double dTest2 = Convert.ToDouble("1005,142247");
double dTest3 ;
dTest3 = (dTest1 - dTest2);
На выходе в отладчике dTest3 0.36000000000001364. Откуда хвост, что это, как исправлять?
Это проблема проявляется не только с 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
Очевидно, что при хранении этого числа будет потеряна точность.
Что можно почитать на тему:
Решение:
Для решения сложившейся ситуации есть несколько путей:
Решение в лоб: округлить результат выполнения операций над double/float с помощью Math.Round:
var r = Math.Round(d, 2);
Использовать специальный тип для хранения чисел с плавающей точкой под названием Decimal. Это надёжное и впрочем рекомендуемое решение, но нужно учесть, что при больших вычислениях может быть незначитедьная потеря в производительности
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Можно ли как-то сделать так, что бы не вызывался Dispose при использовании оберток StreamReader / StreamWriter?
вот моя настройка nhibernate где я говорю, что не выводить записи у которых "IsDeleted = true" в БД :
Подскажите, как можно подсчитать количество символов в поле типа TEXT в БД PostgreSQL, подсчитать количество символов без пробелов, HTML-ссылок, спецсимволов?