Картинка блога

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

2.0 != 1.6+0.4

Это скриншот из панели WatchList VisualStudio 2010, получается, что 1.6+0.4 не равно 2, если используется тип float. Попробую объяснить.

Где ждать проблему?

Первый раз с такой проблемой я столкнулся, когда писал игры на Flash (парадоксально, то что сейчас тоже писал игру 🙂 ). Рассчитывалось движение монетки в игре Pajazzo. Так как сервер сам выбирал куда попадет монетка, нужно было создать карту «сила заброса-заработок» и после этого выбирать один из подходящих путей. При переходе с Flash 8 на Flash 9, движение монеток стало другим и рассчитанный путь не работал в новой версии. Дело оказалось именно в способе округления чисел с плавающей точкой. Решение было неоднозначным, я округлял числа сам примерно таким образом: (int(x*100)+int(offsetX*100))/100. Таким образом я округлял число до двух чисел после запятой.

В C# схожая ситуация, но тут есть специальный метод. Из


if (x + iterator.X <= size.X && y + iterator.Y <= size.Y) { }

получаем:


if (Math.Round(x + iterator.X, 1) <= Math.Round(size.X,1) && Math.Round(y + iterator.Y,1) <= Math.Round(size.Y,1)) { }

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

Почему же 1.6+0.4!=2.0?

Еще на лекциях по информатике, мы на бумаге преобразовывали десятеричные числа в двуричные. С целыми числами тут все в порядке:

1b=1, 10b=2, 11b=3 и т.д. А вот вещественные числа могут хранить только примерное значение, так: 0.1b=0,5, 0,01b=0,25, 0,11b=0,75 и т.д. Чтобы хранить числа «правильно» (как думаю, double в C#) введена мантисса — это значение говорит, где поставить запятую и разделить его дробную часть от целой. Таким образом, числа хранятся как целые, а потом доставляется запятая в нужном месте.

Метки:, ,