Понимание данного расчета (литье + умножение)

(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);