Подтвердить что ты не робот

Целочисленное преобразование в С#

        string[] strArray = new string[10] { "21.65", "30.90", "20.42", "10.00", "14.87", "72.19", "36.00", "45.11", "18.66", "22.22" };
        float temp = 0.0f;
        Int32 resConvert = 0;
        Int32 resCast = 0;
        for (int i = 0; i < strArray.Length; i++)
        {
            float.TryParse(strArray[i], out temp);
            resConvert = Convert.ToInt32(temp * 100);
            resCast = (Int32)(temp * 100);
            Console.WriteLine("Convert: " + resConvert + " ExplCast: " + resCast);
        }

Ans:

   Convert: 2165 ExplCast: 2164   // ??
   Convert: 3090 ExplCast: 3089   // ??
   Convert: 2042 ExplCast: 2042
   Convert: 1000 ExplCast: 1000
   Convert: 1487 ExplCast: 1486   //??
   Convert: 7219 ExplCast: 7219
   Convert: 3600 ExplCast: 3600
   Convert: 4511 ExplCast: 4511
   Convert: 1866 ExplCast: 1865   //??
   Convert: 2222 ExplCast: 2221   //??

Почему значение иногда отличается при выполнении Explicit Cast, но не всегда. Любая причина?

4b9b3361

Ответ 1

Чтобы взять один пример, формат 21.65 в float фактически представлен числом, например 21.6499999. Convert.ToInt32 округляет число до ближайшего целого числа, давая 21.65, тогда как явный листинг (Int32) просто обрезает (округляет к нулю), поэтому вы получаете 21.64.

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

Ответ 2

Convert.ToInt32 раунды до ближайшего целого, прямой приведение просто обрезает число.

Итак, если вы из-за неточности с плавающей запятой имеете значение 2165.99999whatever вместо 2165.0, прямой литье усекает все после плавающей запятой, а Convert.ToInt32 округляется до ближайшего целого.

Пример:
22.22f * 100.0f приводит к чему-то вроде 2221.99993133544921875.
Таким образом, Convert.ToInt32 будет округлить его вверх до ожидаемого значения 2222, в то время как литье усекает его до 2221.

45.11f * 100.0f, с другой стороны, имеет значение около 4511.00006103515625,
который Convert.ToInt32 округляет вниз, что приводит к 4511, тому же результату, что и при литье.

Ответ 3

Следуя ответу Botz3000, соответствующие разделы из MSDN:

Метод Convert.ToInt32 (одиночный)

Возвращаемое значение Тип: System.Int32, округленное до ближайшего 32-битного целое число со знаком. Если значение находится на полпути между двумя целыми числами, четное число возвращается; то есть 4,5 преобразуется в 4, а 5.5 - преобразован в 6.

Явная таблица числовых преобразований

• Когда вы конвертируете из значения double или float в интегральный тип, значение округляется до нуля до ближайшего целочисленного значения. Если результирующее значение интеграла находится вне диапазона адресата значение, результат зависит от контекста проверки переполнения. В проверенный контекст, генерируется исключение OverflowException, в то время как неконтролируемый контекст, результатом является неопределенное значение типа назначения.

Ответ 4

Я думаю, вы обнаружите, что проблема вызвана неточностями точности с плавающей запятой, в частности с помощью float. При использовании decimal проблема исчезнет. Существует действительно хороший ответ на различия между десятичными и двойными (и float) здесь: decimal vs double! - Кого я должен использовать и когда?

Ответ 5

Вызов Convert.ToInt32 похож на вызов:

(int) Math.Round(floatValue, 0);

Прямое кастинг - это вызов

(int) Math.Floor(float);

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

Итак: 21,65 * 100 = 2164,9999 Покрытие этого значения должно дать вам целое число, которое меньше или равно 2164,9... т.е.: 2164

Округление 2164.99, с другой стороны, даст вам: 2165

Здесь вы можете увидеть эффект:

Console.WriteLine(Math.Round(21.65f*100));  //2165
Console.WriteLine(Math.Floor(21.65f*100));  //2164

Использование двойников вместо float (более точность, но не бесконечная):

Console.WriteLine(Math.Round(21.65d*100));  //2165
Console.WriteLine(Math.Floor(21.65d*100));  //2165