¿Por qué la división de enteros en C # devuelve un entero y no un flotante?

131

¿Alguien sabe por qué la división de enteros en C # devuelve un entero y no un flotante? ¿Cuál es la idea detrás de esto? (¿Es solo un legado de C / C ++?)

C ª#:

float x = 13 / 4;   
//== operator is overridden here to use epsilon compare
if (x == 3.0)
   print 'Hello world';

El resultado de este código sería:

'Hello world'

Estrictamente hablando, no existe una división entera (la división por definición es una operación que produce un número racional, los enteros son un subconjunto muy pequeño de los cuales).

BanditoConejito
fuente
46
porque es integerdivisión, no floating pointdivisión.
Hunter McMillen
tiene que (en VB.Net) se implementa de manera diferente en una forma matemática natural donde todo el resultado de la operación de división es un número irracional.
BanditoBunny 01 de
3
Creo que te refieres a números racionales . Ver wikipedia : dividir dos enteros puede dar como resultado un resto. Para completar la división del resto, el sistema numérico se extiende para incluir fracciones o números racionales como se les llama más generalmente.
crashmstr
66
Esta es la razón por la que no soy fanático de "copiar sintaxis" en idiomas. Vengo de VB pensando que "C # es .NET" no "C # es como C". Mi error, supongo, pero en este caso prefiero la forma VB. Si pasaron por la molestia de generar un error de compilación al usar tipos simples no inicializados (ni siquiera aparece una advertencia en C), entonces ¿por qué no avisarle cuando está asignando división entera a un flotante?
darda

Respuestas:

96

Si bien es común que el nuevo programador cometa este error de realizar la división de enteros cuando en realidad pretendían usar la división de punto flotante, en la práctica la división de enteros es una operación muy común. Si asumes que la gente rara vez lo usa, y que cada vez que haces una división, siempre necesitarás recordar lanzar a puntos flotantes, estás equivocado.

En primer lugar, la división de enteros es bastante más rápida, por lo que si solo necesita un resultado de número entero, uno querría usar el algoritmo más eficiente.

En segundo lugar, hay varios algoritmos que usan la división de enteros, y si el resultado de la división siempre fue un número de coma flotante, se vería obligado a redondear el resultado cada vez. Un ejemplo fuera de mi cabeza es cambiar la base de un número. Calcular cada dígito implica la división entera de un número junto con el resto, en lugar de la división de coma flotante del número.

Debido a estas (y otras razones relacionadas), la división de enteros resulta en un entero. Si desea obtener la división de coma flotante de dos enteros, solo deberá recordar lanzar uno a double/ float/ decimal.

Servy
fuente
55
En VB.Net .Net arquitectos tomaron otra decisión: / - siempre una división flotante, \ - división entera, por lo que es un poco inconsistente, excepto si considera el legado de C ++;
BanditoBunny 01 de
44
Puede determinar, en tiempo de compilación, si el /operador realizará una división entera o de coma flotante (a menos que esté usando dinámica). Si es difícil para usted descubrirlo porque está haciendo mucho en esa línea, le sugiero dividir esa línea en varias líneas para que sea más fácil averiguar si los operandos son enteros o de tipo coma flotante. Los futuros lectores de su código probablemente lo apreciarán.
Servy 01 de
55
Personalmente, me parece problemático que siempre tenga que pensar cuáles son las variables que estoy dividiendo, lo considero un mal uso de mi atención.
BanditoBunny
8
@pelesl Como sería un gran cambio radical hacer aquello para lo cual se rompería un número astronómico de programas, puedo decir con total confianza que nunca sucederá en C #. Ese es el tipo de cosas que hay que hacer desde el día 1 en un idioma o nada en absoluto.
Servy
2
@Servy: Hay muchas cosas así en C, C ++ y C #. Personalmente, creo que C # sería un mejor lenguaje si hubiera habido un operador diferente para la división de enteros y, para evitar que el código legítimo produzca un comportamiento sorprendente, el int/intoperador era simplemente ilegal [con un diagnóstico que especifica que el código debe emitir un operando o usar el otro operador, dependiendo del comportamiento deseado]. Si hubiera alguna otra buena secuencia de tokens disponible para la división de enteros, podría ser posible desaprobar el uso de /ese propósito, pero no sé qué sería práctico.
supercat
77

Ver la especificación de C # . Hay tres tipos de operadores de división.

  • División entera
  • División de punto flotante
  • División decimal

En su caso tenemos la división Integer, con las siguientes reglas aplicadas:

La división redondea el resultado hacia cero, y el valor absoluto del resultado es el entero más grande posible que es menor que el valor absoluto del cociente de los dos operandos. El resultado es cero o positivo cuando los dos operandos tienen el mismo signo y cero o negativo cuando los dos operandos tienen signos opuestos.

Creo que la razón por la que C # usa este tipo de división para enteros (algunos lenguajes devuelven resultados flotantes) es el hardware: la división de enteros es más rápida y sencilla.

Sergey Berezovskiy
fuente
¿Qué idiomas devuelven resultado flotante? @SergeyBerezovskiy
Ilaria
40

Cada tipo de datos es capaz de sobrecargar a cada operador. Si tanto el numerador como el denominador son enteros, el tipo entero realizará la operación de división y devolverá un tipo entero. Si desea la división de coma flotante, debe emitir uno o más números a tipos de coma flotante antes de dividirlos. Por ejemplo:

int x = 13;
int y = 4;
float x = (float)y / (float)z;

o, si está usando literales:

float x = 13f / 4f;

Tenga en cuenta que los puntos flotantes no son precisos. Si te importa la precisión, usa algo como el tipo decimal, en su lugar.

Steven Doggart
fuente
1
+1 por mencionar que solo un término necesita ser flotante para hacer la división de coma flotante.
Xynariz
Obviamente, su afirmación sobre la precisión es la correcta en el contexto del aprendizaje y en hacer que no sea complicado de entender. Como debemos ser lo más precisos posible en nuestros trabajos, todavía quiero aclarar la precisión: según IEE 754-1985, PUEDES obtener un resultado exacto (aunque este no es el caso). Puede obtener un resultado preciso, cuando los valores de cálculo se han presentado exactamente antes y el resultado es, simplemente hablando, una suma de potencias de 2. Aunque no puede ser una mejor práctica confiar en esa precisión en esos casos especiales.
L. Monty
Suma de precisión: la posibilidad de obtener un resultado exacto mejora drásticamente ya que el resultado es cercano a 1 o -1. Puede ser un poco confuso que esta capacidad de propagación siga siendo 0 ya que hay números infinitos y un número finito de resultados, que se pueden presentar exactamente. :)
L. Monty
1
@ L.Monty gracias por mencionarlo. Aprendí más sobre los puntos flotantes desde que escribí esta respuesta y el punto que está haciendo es justo. Técnicamente, todavía diría que mi afirmación "los puntos flotantes no son precisos" es aceptable, en el sentido de que solo porque algo puede ser exacto a veces no significa que, en general, sea exacto. Como dicen, un reloj roto es correcto dos veces al día, pero nunca llamaría a uno un instrumento de precisión. De hecho, estoy bastante sorprendido de que esa parte te haya molestado más que mi sugerencia de que el tipo decimal es preciso.
Steven Doggart
Los decimales son imprecisos, por las mismas razones que los flotadores; es solo que los flotadores son de base 2 y los decimales son de base 10. Por ejemplo, un tipo decimal no puede contener con precisión el valor preciso de 1/3.
Steven Doggart
11

Como no utiliza ningún sufijo, los literales 13y 4se interpretan como enteros:

Manual :

Si el literal no tiene sufijo, que tiene el primero de este tipo en el que su valor se puede representar: int, uint, long, ulong.

Por lo tanto, dado que declaras 13como entero, se realizará la división de enteros:

Manual :

Para una operación de la forma x / y, la resolución de sobrecarga del operador binario se aplica para seleccionar una implementación de operador específica. Los operandos se convierten a los tipos de parámetros del operador seleccionado, y el tipo del resultado es el tipo de retorno del operador.

Los operadores de división predefinidos se enumeran a continuación. Todos los operadores calculan el cociente de x e y.

División entera:

int operator /(int x, int y);
uint operator /(uint x, uint y);
long operator /(long x, long y);
ulong operator /(ulong x, ulong y);

Y así ocurre el redondeo:

La división redondea el resultado hacia cero, y el valor absoluto del resultado es el entero más grande posible que es menor que el valor absoluto del cociente de los dos operandos. El resultado es cero o positivo cuando los dos operandos tienen el mismo signo y cero o negativo cuando los dos operandos tienen signos opuestos.

Si haces lo siguiente:

int x = 13f / 4f;

Recibirá un error del compilador, ya que una división de punto flotante (el /operador de 13f) da como resultado un flotante, que no se puede convertir a int implícitamente.

Si desea que la división sea una división de punto flotante, deberá hacer que el resultado sea flotante:

float x = 13 / 4;

Tenga en cuenta que aún dividirá enteros, que se convertirán implícitamente en flotantes: el resultado será 3.0. Para declarar explícitamente los operandos como flotantes, utilizando el fsufijo ( 13f, 4f).

CodeCaster
fuente
+1 por explicar que puedes tener la respuesta como flotante pero aún así hacer una división entera. Además, otra forma común en la que he visto forzar la división de punto flotante es multiplicar el primer término de la división por 1.0.
Xynariz
8

Es solo una operación básica .

Recuerda cuando aprendiste a dividir. Al principio lo resolvimos 9/6 = 1 with remainder 3.

9 / 6 == 1  //true
9 % 6 == 3 // true

El / operador en combinación con el% operador se utilizan para recuperar esos valores.

L. Monty
fuente
6

Puede ser útil:

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
eozten
fuente
Intente agregar algunas explicaciones para su respuesta
NetStarter
La última expresión producirá 2.5, no 2.
Lasse V. Karlsen
Sí, mal escrito. Gracias.
eozten
4

El resultado siempre será del tipo que tenga el mayor rango del numerador y el denominador. Las excepciones son byte y short, que producen 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

No existe una conversión implícita entre los tipos de coma flotante y el tipo decimal, por lo que no se permite la división entre ellos. Debe emitir y decidir explícitamente cuál desea (Decimal tiene más precisión y un rango más pequeño en comparación con los tipos de punto flotante).

zm
fuente