¿Por qué dividir dos int no produce el valor correcto cuando se asigna al doble?

¿Cómo es eso en el siguiente fragmento?

int a = 7; int b = 3; double c = 0; c = a / b; 

c termina teniendo el valor 2, en lugar de 2.3333, como cabría esperar. Si b son dobles, la respuesta pasa a 2.333. Pero seguramente porque c ya es un doble, ¿debería haber funcionado con números enteros?

Entonces, ¿cómo es que int/int=double no funciona?

Esto se debe a que está utilizando la versión de división entera de operator/ , que toma 2 int s y devuelve un int . Para utilizar la versión double , que devuelve un double , al menos uno de los int s debe convertirse explícitamente en un double .

 c = a/(double)b; 

Aquí está:

a) Dividir dos int s realiza la división entera siempre. Entonces, el resultado de a/b en su caso solo puede ser un int .

Si desea mantener a y b como int s, sin embargo, divídalos por completo, debe convertir al menos uno de ellos al doble: (double)a/b o a/(double)b o (double)a/(double)b .

b) c es un valor double , por lo que puede aceptar un valor int en la asignación: el int se convierte automáticamente en el double y se asigna a c .

c) Recuerde que al asignar, la expresión a la derecha de = se calcula primero (según la regla (a) anterior, y sin tener en cuenta la variable a la izquierda de = ) y luego se asigna a la variable a la izquierda de = ( de acuerdo con (b) arriba). Creo que esto completa la imagen.

Con muy pocas excepciones (solo puedo pensar en una), C ++ determina el significado completo de una expresión (o subexpresión) de la expresión misma. Lo que hagas con los resultados de la expresión no importa. En tu caso, en la expresión a / b , no hay un double a la vista; todo es int . Entonces el comstackdor usa la división de enteros. Solo cuando tiene el resultado, considera qué hacer con él y lo convierte en double .

Cuando divide dos enteros, el resultado será un entero, independientemente del hecho de que lo almacene en un doble.

c es una variable double , pero el valor que se le asigna es un valor int porque resulta de la división de dos int s, que le da “división entera” (descartando el rest). Entonces, ¿qué sucede en la línea c=a/b es

  1. a/b se evalúa, creando un temporal de tipo int
  2. el valor del temporal se asigna a c después de la conversión para escribir double .

El valor de a/b se determina sin referencia a su contexto (asignación a double ).

El operador / se puede usar para división entera o división de coma flotante. Le está dando dos operandos enteros, por lo que está haciendo una división entera y luego el resultado se almacena en un doble.

En el lenguaje C ++, el resultado de la subexpresion nunca se ve afectado por el contexto circundante (con raras excepciones). Este es uno de los principios que el lenguaje sigue cuidadosamente. La expresión c = a / b contiene una subexpresión independiente a / b , que se interpreta independientemente de cualquier cosa fuera de esa subexpresión. Al lenguaje no le importa que luego le asigne el resultado a un double . a / b es una división entera. Cualquier otra cosa no importa. Verá este principio seguido en muchas esquinas de la especificación del lenguaje. Así es cómo funciona C ++ (y C).

Un ejemplo de una excepción que mencioné anteriormente es la función de asignación / inicialización del puntero en situaciones con sobrecarga de funciones

 void foo(int); void foo(double); void (*p)(double) = &foo; // automatically selects `foo(fouble)` 

Este es un contexto donde el lado izquierdo de una asignación / inicialización afecta el comportamiento del lado derecho. (Además, la inicialización de referencia a matriz evita la disminución del tipo de matriz, que es otro ejemplo de comportamiento similar). En todos los demás casos, el lado derecho ignora por completo el lado izquierdo.

Esto es técnicamente un idioma dependiente, pero casi todos los idiomas tratan este tema de la misma manera. Cuando hay una discrepancia de tipo entre dos tipos de datos en una expresión, la mayoría de los idiomas intentarán convertir los datos en un lado de = para hacer coincidir los datos del otro lado de acuerdo con un conjunto de reglas predefinidas.

Al dividir dos números del mismo tipo (enteros, dobles, etc.) el resultado siempre será del mismo tipo (por lo que ‘int / int’ siempre dará como resultado int).

En este caso, tiene double var = integer result que arroja el resultado entero a un doble después del cálculo, en cuyo caso los datos fraccionarios ya se han perdido. (la mayoría de los idiomas harán esta conversión para evitar imprecisiones de tipo sin generar una excepción o error).

Si desea mantener el resultado como un doble, va a querer crear una situación donde tenga double var = double result

La forma más fácil de hacerlo es forzar la expresión en el lado derecho de una ecuación para convertir a doble:

c = a/(double)b

La división entre un número entero y un doble dará como resultado el entero al doble (tenga en cuenta que al hacer las matemáticas, el comstackdor a menudo se “elevará” al tipo de datos más específico para evitar la pérdida de datos).

Después del upcast, a terminará como un doble y ahora tienes división entre dos dobles. Esto creará la división y asignación deseada.

OTRA VEZ, tenga en cuenta que esto es específico del idioma (e incluso puede ser específico del comstackdor), sin embargo, casi todos los idiomas (sin duda todos los que se me ocurren de la cabeza) tratan este ejemplo de forma idéntica.