Почему целочисленное деление в C# возвращает целое число, а не число с плавающей точкой?
Кто-нибудь знает, почему целочисленное деление в C# возвращает целое число, а не число с плавающей точкой? Какая идея стоит за этим? (Это только наследие C/C++?)
В C#:
float x = 13 / 4;
//imagine I used have an overridden == operator here to use epsilon compare
if (x == 3.0)
print 'Hello world';
Результатом этого кода будет:
'Hello world'
Строго говоря, не существует такого понятия, как целочисленное деление (деление по определению - это операция, которая производит рациональное число, целые числа являются очень небольшим подмножеством из них.)
7 ответов
Хотя для новых программистов характерна ошибка при выполнении целочисленного деления, когда они фактически намеревались использовать деление с плавающей запятой, на практике целочисленное деление является очень распространенной операцией. Если вы предполагаете, что люди редко используют его, и что каждый раз, когда вы делаете деление, вам всегда нужно помнить, чтобы приводить к плавающим точкам, вы ошибаетесь.
Во-первых, целочисленное деление происходит немного быстрее, поэтому, если вам нужен только результат с целым числом, можно использовать более эффективный алгоритм.
Во-вторых, существует целый ряд алгоритмов, использующих целочисленное деление, и если результатом деления всегда было число с плавающей запятой, вам придется каждый раз округлять результат. Один пример с макушки головы - это изменение базы числа. Вычисление каждой цифры включает в себя целочисленное деление числа вместе с остатком, а не деление числа с плавающей запятой.
Из-за этих (и других связанных) причин целочисленное деление приводит к целому числу. Если вы хотите получить деление с плавающей точкой на два целых числа, вам просто нужно помнить, чтобы привести одно к double
/float
/decimal
,
Смотрите спецификацию C#. Есть три типа операторов деления
- Целочисленное деление
- Деление с плавающей точкой
- Десятичное деление
В вашем случае у нас есть целочисленное деление с применением следующих правил:
Деление округляет результат до нуля, и абсолютное значение результата является наибольшим возможным целым числом, которое меньше абсолютного значения отношения двух операндов. Результатом является ноль или положительный результат, когда два операнда имеют одинаковый знак, и ноль или отрицательный результат, если два операнда имеют противоположные знаки.
Я думаю, что причина, по которой C# использует этот тип деления для целых чисел (некоторые языки возвращают плавающий результат), заключается в аппаратном - деление целых чисел происходит быстрее и проще.
Каждый тип данных способен перегружать каждого оператора. Если числитель и знаменатель являются целыми числами, целочисленный тип выполнит операцию деления и вернет целочисленный тип. Если вы хотите разделить числа с плавающей запятой, вы должны привести одно или несколько чисел к типам с плавающей запятой перед их делением. Например:
int x = 13;
int y = 4;
float x = (float)y / (float)z;
или, если вы используете литералы:
float x = 13f / 4f;
Имейте в виду, плавающие точки не являются точными. Если вам важна точность, используйте что-то вроде десятичного типа.
Может быть полезно:
double a = 5.0/2.0;
Console.WriteLine (a); // 2.5
double b = 5/2;
Console.WriteLine (b); // 2
int c = 5/2;
Console.WriteLine (c); // 2
double d = 5f/2f;
Console.WriteLine (d); // 2.5
Поскольку вы не используете суффикс, литералы 13
а также 4
интерпретируются как целое число:
Если литерал не имеет суффикса, он имеет первый из этих типов, в котором его значение может быть представлено:
int
,uint
,long
,ulong
,
Таким образом, так как вы объявляете 13
как целое число будет выполнено целочисленное деление:
Для операции вида x / y применяется разрешение перегрузки бинарного оператора для выбора конкретной реализации оператора. Операнды преобразуются в типы параметров выбранного оператора, а тип результата является типом возврата оператора.
Предопределенные операторы деления перечислены ниже. Все операторы вычисляют отношение x и y.
Целочисленное деление:
int operator /(int x, int y); uint operator /(uint x, uint y); long operator /(long x, long y); ulong operator /(ulong x, ulong y);
И так происходит округление:
Деление округляет результат до нуля, и абсолютное значение результата является наибольшим возможным целым числом, которое меньше абсолютного значения отношения двух операндов. Результатом является ноль или положительный результат, когда два операнда имеют одинаковый знак, и ноль или отрицательный результат, если два операнда имеют противоположные знаки.
Если вы делаете следующее:
int x = 13f / 4f;
Вы получите ошибку компилятора, так как деление с плавающей запятой (/
оператор 13f
) приводит к float, который не может быть приведен к int неявно.
Если вы хотите, чтобы деление было делением с плавающей точкой, вы должны сделать результат плавающим:
float x = 13 / 4;
Обратите внимание, что вы по-прежнему делите целые числа, которые будут неявно приводиться к плавающему: результат будет 3.0
, Чтобы явно объявить операнды как плавающие, используя f
суффикс (13f
, 4f
).
Это просто базовая операция.
Помните, когда вы научились делить. В начале мы решили 9/6 = 1 with remainder 3
,
9 / 6 == 1 //true
9 % 6 == 3 // true
/-Оператор в сочетании с%-оператором используются для получения этих значений.
Результат всегда будет иметь тип, который имеет больший диапазон числителя и знаменателя. Исключением являются байты и короткие, которые выдают int (Int32).
var a = (byte)5 / (byte)2; // 2 (Int32)
var b = (short)5 / (byte)2; // 2 (Int32)
var c = 5 / 2; // 2 (Int32)
var d = 5 / 2U; // 2 (UInt32)
var e = 5L / 2U; // 2 (Int64)
var f = 5L / 2UL; // 2 (UInt64)
var g = 5F / 2UL; // 2.5 (Single/float)
var h = 5F / 2D; // 2.5 (Double)
var i = 5.0 / 2F; // 2.5 (Double)
var j = 5M / 2; // 2.5 (Decimal)
var k = 5M / 2F; // Not allowed
Не существует неявного преобразования между типами с плавающей точкой и десятичным типом, поэтому деление между ними не допускается. Вы должны явно привести и решить, какой из них вы хотите (десятичная имеет большую точность и меньший диапазон по сравнению с типами с плавающей запятой).
В качестве небольшого трюка, чтобы узнать, что вы получаете, вы можете использовать var, поэтому компилятор сообщит вам, какой тип следует ожидать:
int a = 1;
int b = 2;
var result = a/b;
ваш компилятор сообщит вам, что здесь результат будет иметь тип int.