float ff = (float)31.15;
double dd = 31.15;
var frst = Math.Round(ff, 1, MidpointRounding.AwayFromZero);
var drst = Math.Round(dd, 1, MidpointRounding.AwayFromZero);
первое: 31,1
дрст: 31,2
Может кто-нибудь объяснить почему?
float ff = (float)31.15;
double dd = 31.15;
var frst = Math.Round(ff, 1, MidpointRounding.AwayFromZero);
var drst = Math.Round(dd, 1, MidpointRounding.AwayFromZero);
первое: 31,1
дрст: 31,2
Может кто-нибудь объяснить почему?
Ну, Math.Round
хочет double
, а не float
, вот почему
Math.Round(ff, 1, MidpointRounding.AwayFromZero);
равно
Math.Round((double)ff, 1, MidpointRounding.AwayFromZero);
и если мы проверяем (double)ff
значение (double)ff
Console.Write(((double)ff).ToString("R"));
мы увидим ошибки в действии
31.149999618530273
Наконец, Math.Round(31.149999618530273, 1, MidpointRounding.AwayFromZero) == 31.1
как и ожидалось
В плавающей запятой все числа представляются внутри как дроби, где знаменатель является степенью 2.
(Это похоже на то, как десятичные дроби - это на самом деле дроби со знаменателями степени 10. Таким образом, 31.15
- это просто способ записи дроби 3115/100
)
В плавающей точке 31.15
должен быть представлен внутри как двоичное число. Ближайшая двоичная дробь: 1111.1001001100110011001100110011001100110011001100110011001100...repeating
1100
повторяется (повторяется вечно), и поэтому число будет усечено в зависимости от того, хранится ли оно в двойном или числовом выражении. В формате с плавающей точкой оно усекается до 24 цифр, а в двойном - до 53.
Exact: 1111.100100110011001100110011001100110011001100110011001100110011001100...forever
Float: 1111.10010011001100110011
Double: 1111.1001001100110011001100110011001100110011001100110
Следовательно, вы можете видеть, что двойное число, в которое преобразуется это число, на самом деле немного больше, чем число, в которое оно преобразуется. Таким образом, ясно, что оно не обязательно округляется до одного и того же числа, поскольку это не одно и то же число для начала.
Потому что (float)31.15
не равно (double)31.15
. Арифметика с плавающей точкой почти всегда приводит к ошибкам округления. В частности, округление двойных работает иначе, чем округление поплавка.
В вашем примере 31.15
может быть представлен как 31.144448
при преобразовании в число с float
, но 31.1500000000001
при преобразовании в значение типа double
.
При округлении первого вы получаете 31.1
, а второе - 31.2
.
У вас есть свой ответ здесь !
Перед тем как опубликовать вопрос, вы должны проверить форум, чтобы увидеть, если у вас уже есть ответ на ваш вопрос!
Уже объяснено, почему у нас есть такая проблема с округлением, но стоит упомянуть, что с этим небольшим приемом вы можете избавиться от него:
float ff = (float)31.15;
double dd = 31.15;
var frst = Math.Round(
double.Parse(ff.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture),
1,
MidpointRounding.AwayFromZero);
var drst = Math.Round(dd, 1, MidpointRounding.AwayFromZero);
Тогда результат должен быть таким же:
3.2
3.2
поэтому, пока (double)ff
double.Parse(ff.ToString())
проблему округления, double.Parse(ff.ToString())
не делает этого, потому что преобразование из float
в double
избегается.