2010-08-15 22 views
7

He estado programando durante un tiempo en C++, pero de repente tuve una duda y quería aclararme con la comunidad de Stackoverflow.División entera vs división flotante -> ¿Quién es responsable de proporcionar el resultado?

Cuando un entero se divide por otro entero, todos sabemos que el resultado es un número entero y, como siempre, un flotante dividido por un flotante también es un flotante.

¿Pero quién es responsable de proporcionar este resultado? ¿Es el compilador o la instrucción DIV?

+1

¿Está dividiendo constantes o variables? –

Respuesta

3

El compilador decidirá en tiempo de compilación qué forma de división se requiere en función de los tipos de las variables utilizadas; al final del día se involucrará una instrucción DIV (o FDIV) de una forma u otra.

+1

A menos que las variables sean constantes de tiempo de compilación, en cuyo caso el compilador se "responsabilizará" y no se producirán instrucciones. – Potatoswatter

+0

@Potatoswatter: No del todo cierto; el compilador DEBE evaluar expresiones constantes integrales y PUEDE evaluar otras expresiones constantes. Es decir. 'float x = 1.0/3.0' puede ser evaluado por el compilador, o puede resultar en una compilación del compilador FDIV. – MSalters

11

Eso depende de si su arquitectura tiene una instrucción DIV. Si su arquitectura tiene instrucciones de división de coma flotante y entera, el compilador emitirá las instrucciones correctas para el caso especificado por el código. El estándar de idioma especifica las reglas para la promoción de tipo y si debe usarse la división de coma flotante o entero en cada situación posible.

Si solo tiene una instrucción de división de enteros, o solo una instrucción de división de coma flotante, el compilador insertará algún código o generará una llamada a una biblioteca de soporte matemático para manejar la división. Las instrucciones de división son notoriamente lentas, por lo que la mayoría de los compiladores intentarán optimizarlas si es posible (por ejemplo, reemplazar con instrucciones de desplazamiento, o calcular previamente el resultado de una división de constantes de tiempo de compilación).

3

Las instrucciones de división de hardware casi nunca incluyen la conversión entre entero y coma flotante. Si recibe instrucciones divididas (a veces quedan fuera, porque un circuito dividido es grande y complicado), es prácticamente seguro que serán "dividir int por int, producir int" y "dividir flotación por flotación, producir flotación" . Y generalmente será que ambas entradas y la salida son todas del mismo tamaño, también.

El compilador es responsable de generar cualquier operación escrita en el código fuente, además de estas primitivas. Por ejemplo, en C, si divide un float por un int, el compilador emitirá una conversión int-to-float y luego una división float.

(Existen excepciones extrañas. No lo sé, pero no me gustaría dejar atrás el VAX para tener instrucciones de tipo "dividir la flotación por int". El Itanium en realidad no tenía una instrucción dividida, pero su "ayudante dividir" fue única de punto flotante, que tuvo que dividir número entero falsa en la parte superior de la división de flotación!)

+0

Heh. En cuanto a las "extravagantes excepciones", como las llamas, probablemente hayas usado una solo ahora para escribir esta respuesta. La instrucción x86 (o más precisamente x87) para * divide float por int * se llama FIDIV. – slacker

+1

Y no es el único punto donde la extravagancia de x86 avergüenza a VAX :). – slacker

+0

¡Hah! Bueno, sería el x87. No hicieron nada, de la manera normal con esa cosa. – zwol

0

Prácticamente

el estándar C99 define "Cuando enteros se dividen, el resultado de la/el operador es el cociente algebraico con cualquier parte fraccional descartada ". Y añade en una nota que "este es a menudo llamado 'truncamiento hacia cero.'"

Historia

Históricamente, la especificación del lenguaje es responsable.

Pascal defines its operators por lo que el uso de / para la división siempre devuelve un real (incluso si se utiliza para dividir números enteros 2), y si se desea dividir números enteros y obtener un resultado entero, se utiliza el operador div lugar. (Visual Basic tiene una distinción similar y utiliza el operador \ para la división entera que devuelve un resultado entero.)

En C, se decidió que la misma distinción se debe realizar mediante la conversión de uno de los operandos enteros a float si desea un resultado de punto flotante. Se ha convertido en una convención tratar los tipos enteros en lugar de los puntos flotantes de la manera en que se describen en muchos lenguajes derivados de C. Sospecho que esta convención puede haberse originado en Fortran.

1

Una de las reglas más importantes en el estándar de C++ es el "como si" regla:

Las descripciones semánticas en esta Norma Internacional definen una máquina abstracta no determinista con parámetros. Esta Norma Internacional no exige ningún requisito sobre la estructura de implementaciones conformes. En particular, no necesitan copiar o emular la estructura de la máquina abstracta. Por el contrario, se requieren implementaciones adecuadas para emular (solo) el comportamiento observable de la máquina abstracta como se explica a continuación.

Cuál en relación a su pregunta significa que no importa qué componente hace la división, mientras se hace. Puede realizarse mediante un código de máquina DIV, puede realizarse mediante un código más complicado si no hay una instrucción adecuada para el procesador en cuestión.

También puede:

  1. Reemplazar la operación con una operación de desplazamiento de bit en su caso y es probable que sea más rápido.
  2. Reemplace la operación con un literal si se puede computar en tiempo de compilación o una tarea si, p. al procesar x/y, se puede mostrar en tiempo de compilación que y siempre será 1.
  3. Reemplace la operación con un lanzamiento de excepción si se puede mostrar en tiempo de compilación que siempre será una división entera por cero.
+0

¿Podría negarse a compilar si pudiera deducir en tiempo de compilación que el cociente siempre sería cero, o es legítimo que un programa C totalmente conformativo contenga una instrucción como x = 1/(x-x); siempre que tal instrucción nunca se ejecute en realidad? – supercat

+0

@supercat: Es perfectamente legítimo. Es la * ejecución * de una división por cero que causa un comportamiento indefinido. – slacker

+1

@supercat si quieres producir un programa que siempre tenga errores, ¡es asunto tuyo! Después de todo, es difícil enseñarle a la gente sobre los errores sin ellos (aunque en algunos idiomas tienes que hacer cosas como 'if (true == false)' para engañar a los compiladores y permitirte compilar). Sin duda sería razonable y útil que el compilador produzca una advertencia no estandarizada. –

1

Su pregunta realmente no tiene sentido. La instrucción DIV no hace nada por sí mismo. No importa lo fuerte que grita a él, incluso si se intenta sobornarlo, no se hace responsable de nada

Cuando se programa en un lenguaje de programación [X], es responsabilidad exclusiva de la [ X] compilador a hacer un programa que hace lo que describió en el código fuente.

Si se solicita una división, el compilador decide cómo hacer una división. Eso podría suceder al generar el código de operación para la instrucción DIV, si la CPU a la que apunta tiene una. Puede ser precomputando la división en tiempo de compilación e insertando el resultado directamente en el programa (asumiendo que ambos operandos son conocidos en tiempo de compilación), o puede hacerse generando una secuencia de instrucciones que juntas emulan a Divison.

Pero es siempre hasta el compilador. Su programa C++ no tiene ningún efecto a menos que se interprete de acuerdo con el estándar C++. Si lo interpreta como un archivo de texto sin formato, no hace nada. Si su compilador lo interpreta como un programa Java, lo estrangulará y rechazará.

Y la instrucción DIV no sabe nada sobre el estándar C++.Un compilador C++, por otro lado, es escrito con el único propósito de comprender el estándar C++ y transformar el código de acuerdo con él.

El compilador es siempre responsable.