¿Por qué la división de enteros resulta en un entero?

Aprendimos en Introducción a la programación que si divide dos números enteros, siempre obtiene un número entero. Para solucionar el problema, convierta al menos uno de esos enteros en flotante.

¿Por qué el compilador no entiende que quiero que el resultado sea un número decimal?

Comentarios

  • Tal vez quieras que el resultado sea un número entero. ¿Cómo puede notar la diferencia?
  • Porque el estándar C ++ lo dice.
  • @soandos: La forma en que Pascal y Python lo hacen: tienen un operador de división de enteros distinto, pero el operador de división estándar siempre devuelve el resultado matemáticamente correcto. (O – para el pedante – tan correcto como se puede obtener dado el limitaciones de las matemáticas FP.)
  • ¿Cómo sabría el compilador que desea que el resultado sea un número decimal? Hay razones válidas para querer un número entero.
  • @soandos En Python, // es el operador de división de enteros y # es un comentario de una sola línea. En Pascal, el operador de división de enteros es la palabra clave div. Ambos funcionan bastante bien para sus respectivos idiomas ges. C probablemente hace lo peor posible: un operador que puede hacer dos cosas completamente diferentes según un contexto arbitrario.

Respuesta

¿Por qué el compilador no entiende que quiero que el resultado sea un número decimal?

El compilador de C ++ simplemente sigue reglas deterministas y bien definidas como se establece en el estándar C ++. El estándar C ++ tiene estas reglas porque el comité de estándares decidió hacerlo de esa manera.

Podrían haber escrito el estándar para decir que las matemáticas enteras dan como resultado números de punto flotante, o lo hacen solo en el caso de un resto. Sin embargo, eso agrega complejidad: necesito saber de antemano cuál es el resultado, o tal vez convertir de nuevo a un número entero si siempre da un flotante. Tal vez quiero un número entero .

Una de las filosofías centrales de C ++ es «no pagas por lo que no usas . » Si realmente desea la complejidad de mezclar enteros y flotantes (y las instrucciones adicionales de la CPU y los accesos a la memoria, esto implica 1 ), haga el tipo de conversión como mencionó en su pregunta . De lo contrario, apéguese a las matemáticas enteras estándar.

Finalmente, mezclar variables integrales y de punto flotante puede resultar en pérdida de precisión y, a veces, resultados incorrectos, como discuto a continuación. Si desea esto, pague por ello: de lo contrario, el estándar dicta que los compiladores se adhieran a un conjunto estricto de reglas para mezclar tipos de datos. Este es un comportamiento bien definido: como desarrollador de C ++, puedo buscar esto en el estándar y ver cómo funciona.


Básicamente, hay tres formas de hacer lo que estás tratando de hacer, cada uno con ventajas e inconvenientes.

  • Matemáticas de enteros: esto da como resultado resultados truncados durante la división, como averiguó. Si desea la parte decimal, debe tratarla por separado dividiendo, obteniendo el resto y tratando la parte decimal como el resto dividido por el divisor. Esta es una operación un poco más compleja y tiene más variables para hacer malabares.

  • Matemáticas de punto flotante: esto generalmente producirá resultados correctos (suficientes) para valores pequeños, pero puede introducir errores fácilmente con precisión y redondeo, especialmente a medida que aumenta el exponente. Si divide un número grande por un número pequeño, incluso puede causar un desbordamiento o simplemente obtener un resultado incorrecto porque las escalas de los números no se combinan bien entre sí.

  • Haga sus propias matemáticas. Existen clases que manejan precisión extendida de números decimales y racionales . Por lo general, estos serán más lentos que las matemáticas en los tipos integrados, pero en general siguen siendo bastante rápidos y proporcionan matemáticas de precisión arbitraria. El redondeo y otros problemas no son automáticos como lo son con los flotantes IEEE, pero obtienes más control y ciertamente más precisión.

La clave aquí es elegir en función del dominio del problema . Los tres métodos de representar números tienen sus propias ventajas e inconvenientes. ¿Estás usando un contador de bucles? Elija un tipo integral. ¿Representando ubicaciones en el espacio 3D? Probablemente un grupo de carrozas. ¿Quiere realizar un seguimiento del dinero? Utilice un tipo decimal fijo.


1 Las arquitecturas de CPU más populares (p. Ej., x86-64 ) tendrá conjuntos separados de instrucciones que operan en diferentes tipos de registros, como enteros y de coma flotante, además de instrucciones adicionales para convertir entre integrales, de coma flotante y varias representaciones de ellas (con y sin signo, flotante y doble). Algunas de estas operaciones también pueden implicar acceso a la memoria: convertir un valor y almacenarlo en la memoria (su variable). Las matemáticas a nivel de CPU no son tan simples como «entero dentro, flotando fuera.»Si bien agregar dos números enteros puede ser una operación muy simple, posiblemente una sola instrucción, mezclar tipos de datos puede aumentar la complejidad.

Comentarios

  • Dices que el estándar C ++ estipula que ese comportamiento debería ser así. ¿Por qué? ¿No ‘ t facilitaría las cosas decir, » La división de números enteros que no son divisibles de manera uniforme da como resultado flotantes, cualquier otra división es un juego limpio. »
  • @ moonman239 vea mis ediciones.
  • @ moonman239 No para escritores de compiladores. Muchas arquitecturas de CPU de uso común proporcionan un resultado entero cuando se les pide que hagan una división con dos enteros. Tendrían que implementar una verificación de resultados no enteros y luego cambiar para usar el punto flotante más lento Alternativamente, podrían haber pasado a la división de punto flotante y perder el interés de aquellos que querían matemáticas rápidas, aquellos que querían matemáticas precisas y aquellos que estaban acostumbrados a C. Cambiando ahora no es ‘ t una opción porque rompería la compatibilidad con el código existente.
  • No es que esté defendiendo esto como una alternativa, sino haciendo el tipo estático de una expresión dependen de los valores de tiempo de ejecución de los operandos que ‘ no funcionan con el sistema de tipos estáticos de C ++ ‘.
  • @ moonman239: Tener una operación que produzca un tipo diferente dependiendo de los valores de los operandos es una locura.

Respuesta

Esto se debe a la evolución del hardware. En los primeros días de las computadoras, no todas las máquinas tenían una unidad de punto flotante, el hardware simplemente no podía entender la noción de un número de punto flotante. Por supuesto, los números de coma flotante se pueden implementar como una abstracción de software, pero eso tiene desventajas importantes. Toda la aritmética en estas máquinas tenía que ser pura aritmética de enteros por defecto.

Y aún hoy, existe una distinción firme entre unidades aritméticas de enteros y de coma flotante dentro de una CPU. Sus operandos se almacenan en archivos de registro separados para empezar, y una unidad entera está conectada para tomar dos argumentos enteros y producir un resultado entero que termina en un registro entero. Algunas CPU incluso requieren que se almacene un valor entero en la memoria y luego se vuelva a cargar en un registro de punto flotante, antes de que pueda recodificarse en un número de punto flotante, antes de poder realizar una división de punto flotante en él. p> Como tal, la decisión tomada por los desarrolladores de C desde el principio del lenguaje (C ++ simplemente heredó este comportamiento), fue la única decisión apropiada que tomar, y sigue siendo valiosa hoy: si necesita matemáticas de punto flotante, puede usarlo. Si no lo necesita, no tiene que hacerlo.

Comentarios

  • Es triste que la mayoría de las restricciones que existían en el ¡La creación del estándar C ++ es bastante obsoleta hoy! Por ejemplo: » no paga por lo que no usa. » hoy en día, el hardware se da por sentado y todo lo que los usuarios quieren es ejecución!
  • @ mahen23 No todos los usuarios piensan así. Trabajo en un campo donde los programas se ejecutan en miles de núcleos de CPU en paralelo. En esta área, la eficiencia es dinero, tanto en términos de inversiones como en términos de consumo de energía. Un lenguaje como Java no tiene ninguna posibilidad en esa área, mientras que C ++ sí.
  • @ mahen23 No, no es ‘ t – o mejor, solo lo es si observa las arquitecturas de CPU actuales para computadoras de escritorio y superiores. Todavía hay muchos sistemas embebidos que no ‘ to solo admiten parcialmente operaciones de punto flotante, y tanto C como C ++ continúan admitiéndolos para proporcionar la implementación más eficiente posible a menos que usando ensamblador. Por cierto, incluso lenguajes de nivel superior como Python distinguen entre operaciones de números enteros y FP – intente 10 / 3.

Respuesta

10/2 con números enteros le da exactamente 5 – la respuesta correcta.

Con matemáticas de punto flotante, 10/2 podría dar la respuesta *.

En otras palabras, es imposible que los números de coma flotante sean «perfectos» en el hardware actual; solo las matemáticas enteras pueden ser correctas, desafortunadamente no pueden usar decimales, pero es fácil trabajar arounds.

Por ejemplo, en lugar de 4/3, haga (4 * 1000) / (3 * 1000) == 1333. Simplemente dibuje a. en el software cuando muestre la respuesta a su usuario (1.333). Esto le da una respuesta precisa, en lugar de una que sea incorrecta por cierto número de lugares decimales.

Los errores matemáticos de coma flotante pueden sumarse para causar errores significativos; cualquier cosa importante (como finanzas) utilizará matemática entera .

* el ejemplo 10/2 será correcto con matemáticas de coma flotante, pero no puede confiar en ello, muchos otros números dan resultados incorrectos …para obtener más detalles, lea un poco: http://http.cs.berkeley.edu/~wkahan/ieee754status/ieee754.ps El punto es que no puede confiar en la precisión siempre que haya puntos flotantes involucrados

Comentarios

  • Las implementaciones de punto flotante que cumplen con IEEE 754 le darán un resultado exacto para 10/2. De hecho, le darán resultados para cualquier operación que involucre solo operandos enteros que tengan un resultado entero, siempre que los operandos y el resultado se puedan representar exactamente, lo que los números enteros «suficientemente pequeños» pueden hacerlo.
  • @ 5gon12eder hay ‘ s no hay necesidad de elegir nit, yo ‘ estoy tratando de describir un problema complejo en términos simples. El objetivo de admitir valores que no son enteros es tener lugares decimales ( que se puede hacer usando números enteros simplemente multiplicando todo por el número de lugares decimales que desee como demsonstraed).

Respuesta

Aunque técnicamente no es del todo correcto, C ++ todavía se considera un superconjunto de C, se inspiró en él y, como tal, se apropió de algunas de sus propiedades, siendo la división de enteros una de ellas.

C fue principalmente diseñado para ser eficiente y rápido, y los enteros son generalmente mucho más rápido que los puntos flotantes, porque el tipo entero está ligado al hardware, mientras que los puntos flotantes deben calcularse.

Cuando el operando / recibe dos enteros, uno del lado izquierdo y uno del derecho, es posible que ni siquiera haga división, el resultado se puede calcular usando una suma simple y un bucle, preguntando cuántas veces el operando del lado derecho encaja en el operando de la izquierda.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *