Понимание данного расчета (литье + умножение)
(int)((float)10.9 * 10)
оценивается как 108
. Почему?
IMO (int)
-cast следует оценивать после умножения.
Ответы
Ответ 1
Это точно так. Просто 10.9 немного меньше 10.9 из-за того, что представлены числа с плавающей запятой (10.9 не могут быть представлены точно, поэтому вы получите приблизительное значение, которое в данном случае составляет примерно 10.89999999...). Затем кастинг усекает любые цифры после десятичной точки. Итак, вы получаете 108.
Ниже приведены точные значения (полученные с помощью класса Jon Skeet DoubleConverter):
10.9 -> 10.9000000000000003552713678800500929355621337890625
(float) 10.9 -> 10.8999996185302734375
Умножение этого поплавка на 10, а затем отсечение всех десятичных знаков, очевидно, приведет к 108.
Ответ 2
Как ни странно, проблема заключается в том, что выражение вычисляется во время компиляции, по-видимому, используя математику с одинарной точностью, как и ожидалось. Это происходит как в сборке отладки, так и в выпусках:
// this replaces the whole operation
IL_0001: ldc.i4.s 108
IL_0003: stloc.0
IL_0004: ldloc.0
IL_0005: call void [mscorlib]System.Console::WriteLine(int32)
В то время как случай var f =((float)10.9 * 10);int i = (int)f;
по-прежнему оптимизирован для умножения, но с использованием двойной точности. Поскольку бросок выполняется на отдельном шаге, я предполагаю, что он смущает компилятор (??):
IL_000b: ldc.r4 109 // 109 result for the first part
IL_0010: stloc.1
IL_0011: ldloc.1
IL_0012: conv.i4 // separate conversion to int
IL_0013: stloc.2
// rest is printing it
IL_0014: ldloc.1
IL_0015: box [mscorlib]System.Single
IL_001a: ldstr " "
IL_001f: ldloc.2
IL_0020: box [mscorlib]System.Int32
IL_0025: call string [mscorlib]System.String::Concat(object, object, object)
IL_002a: call void [mscorlib]System.Console::WriteLine(string)
Честно говоря, я комментирую, что во втором случае компилятор генерирует неправильный код, поэтому, вероятно, С++ избегает оптимизации кода с плавающей точкой, такого как чума. К счастью, это только проблема для такого рода тестовых кодов, которые могут быть выполнены полностью во время компиляции, фактическое умножение переменной будет в порядке.
Я тоже пробовал это:
Console.WriteLine((int)(float.Parse(Console.ReadLine()) * int.Parse(Console.ReadLine())));
и дал ему 10,9 и 10, и результат составил 108, как ожидалось.
Ответ 3
Результат float
перед тем, как выполнить кастинг в int, составляет 108.9999, потому что 109.0000 не может быть представлен в плавающей запятой. (Как упоминалось в комментариях, это неверно, 109 можно представить просто отлично, но как-то выражение выражается в 108.9999 - все еще применяется)
Значение int
будет равно 108. Почему? Кастинг с плавающей запятой (double
или float
) до int
сокращает десятичную часть. Это не круто. Таким образом, он отключает .9999, и вы получаете 108.
Чтобы закруглить, замените листинг на Math.Round()
или Convert.ToInt32()
.
Convert.ToInt32((float) 10.9 * 10);