В очередной раз столкнулся с проблемой в математике с плавающей точкой, но в этот раз в C#.
Есть код, который должен посчитать бонус ускорения для времени.
В конкрентном случае, где был словлен баг, нужно рассчитать бонус в 10% для 550 секунд.
Имеем следующий код:
int time = 550;
float bonus = 1.1f;
var resultValue = time / bonus;
var value1 = (int) resultValue;
И почти такой же, но немного сокращенный, так как нет смысла держать промежуточную переменную:
int time = 550;
float bonus = 1.1f;
var value2 = (int)(time/ bonus);
Какой значение в реальном мире мы хотим получить? 500, вроде очевидно.
Казалось бы, value1
и value2
должны быть равны, но нет!
Значение value1
равно 500, а вот value2
499.
Почему так?
Тип выражения time/bonus
- это float
, так как bonus
имеет такой тип и (time/bonus).GetType()
честно возвращает System.Single
.
Приведение к int
отбрасывает значимую часть.
Вo float
значение выглядит как 500.0000002
, отбрасываем - получаем 500. Идеально.
НО
Компилятор волен (т.е. зависит от реализации, а точнее от платформы, section 11.2.2) промежуточные значения оставлять с более высокой точностью,
т.е. во втором случае это double
, и результат деление в итоге выглядит как 499.99999998
.
Отбрасываем значимую часть и получаем 499!
По ссылке все, в принципе, хорошо описывается.
И да, для расчета экономических параметров стоит использовать все-таки Decimal
.