Caso 1:
#include <iostream>
int main()
{
double d = 15.50;
std::cout<<(d/0.0)<<std::endl;
}
Se compila sin advertencias ni se imprime inf. Bien, C ++ puede manejar la división por cero ( véalo en vivo ).
Pero,
Caso 2:
#include <iostream>
int main()
{
double d = 15.50;
std::cout<<(d/0)<<std::endl;
}
El compilador da la siguiente advertencia ( véalo en vivo ):
warning: division by zero [-Wdiv-by-zero]
std::cout<<(d/0)<<std::endl;
¿Por qué el compilador da una advertencia en el segundo caso?
Es 0 != 0.0?
Editar:
#include <iostream>
int main()
{
if(0 == 0.0)
std::cout<<"Same"<<std::endl;
else
std::cout<<"Not same"<<std::endl;
}
salida:
Same
c++
gcc
floating-point
divide-by-zero
Jayesh
fuente
fuente

Respuestas:
La división de coma flotante por cero está bien definida por IEEE y da infinito (ya sea positivo o negativo según el valor del numerador (o
NaNpara ± 0) ).Para los números enteros, no hay forma de representar el infinito y el lenguaje define la operación para que tenga un comportamiento indefinido, por lo que el compilador intenta ayudarlo a alejarse de esa ruta.
Sin embargo, en este caso, dado que el numerador es a
double, el divisor (0) también debe promoverse a un doble y no hay razón para dar una advertencia aquí sin dar una advertencia,0.0así que creo que esto es un error del compilador.fuente
d/0,0se convierte al tipo ded.En Standard C ++, ambos casos son comportamientos indefinidos . Puede suceder cualquier cosa, incluido el formateo de su disco duro. No debe esperar o confiar en "return inf. Ok" o cualquier otro comportamiento.
El compilador aparentemente decide dar una advertencia en un caso y no en el otro, pero esto no significa que un código esté bien y el otro no. Es solo una peculiaridad de la generación de advertencias del compilador.
Desde el estándar C ++ 17 [expr.mul] / 4:
fuente
std::numeric_limits<T>::is_iec559estrue, entonces la división por ceroTno es UB (y en la mayoría de las plataformas estrueparadoubleyfloat, aunque para ser portátil, es necesario comprobarlo explícitamente conifoif constexpr).is_iec559comotruesignifica que la implementación está documentando un comportamiento que el estándar deja sin definir. Es solo que este es un caso en el que la documentación de la implementación se puede leer mediante programación. Ni siquiera el único: lo mismo se aplica a losis_modulotipos enteros con signo.Mi mejor suposición para responder a esta pregunta en particular sería que el compilador emite una advertencia antes de realizar la conversión de
intadouble.Entonces, los pasos serían así:
/(T, T2), dondeT=double,T2=int.std::is_integral<T2>::valueestrueyb == 0- esto activa una advertencia.T2adoublePor supuesto, esto es especulación y se basa en especificaciones definidas por el compilador. Desde el punto de vista estándar, estamos tratando con posibles comportamientos indefinidos.
Tenga en cuenta que este es el comportamiento esperado de acuerdo con la documentación de GCC
(por cierto, parece que esta bandera no se puede usar explícitamente en GCC 8.1)
fuente
/para saber que es una división. Si el lado izquierdo hubiera sido unFooobjeto, y hubiera unoperator/(Foo, int), entonces podría ni siquiera ser una división. El compilador solo conoce su división cuando ha elegidobuilt-in / (double, double)usar una conversión implícita del lado derecho. Pero eso significa que NO está haciendo una división porint(0), está haciendo una división pordouble(0).operator /(double, int)es ciertamente aceptable. Luego, dice que la conversión se realiza antes que cualquier otra acción, pero GCC podría hacer una verificación rápida siT2es de tipo enterob == 0y emitir una advertencia si es así. No estoy seguro de si es totalmente compatible con los estándares, pero los compiladores tienen total libertad para definir las advertencias y cuándo deben activarse.operator/(double,int)realmente existe. El compilador puede, por ejemplo, decidir optimizara/bpara constantebreemplazándolo cona * (1/b). Por supuesto, eso significa que ya no está llamandooperator/(double,double)en tiempo de ejecución, sino más rápidooperator*(double,double). Pero ahora es el optimizador el que se tropieza1/0, la constante a la que tendría que alimentarseoperator*No entraré en la debacle UB / no UB en esta respuesta.
Solo quiero señalar eso
0y0.0somos diferentes a pesar de0 == 0.0evaluarlos como verdaderos.0es unintliteral y0.0es undoubleliteral.Sin embargo, en este caso, el resultado final es el mismo:
d/0es una división de punto flotante porquedes doble y, por0lo tanto, se convierte implícitamente en doble.fuente
doublepor unintmedio al queintse conviertedouble, y se especifica en el estándar que0convierte a0.0(conv.fpint / 2)0es lo mismo que0.00 != 0.0?". OP nunca pregunta si son "iguales". También me parece que la intención de la pregunta es si sed/0puede comportar de manera diferente ad/0.0Yo diría que
foo/0y nofoo/0.0son lo mismo. Es decir, el efecto resultante de la primera (división de enteros o división de punto flotante) depende en gran medida del tipo defoo, mientras que no ocurre lo mismo con la segunda (siempre será una división de punto flotante).Si alguno de los dos es UB es irrelevante. Citando el estándar:
(El énfasis es mío)
Considere la advertencia " Sugerir paréntesis alrededor de la asignación usada como valor de verdad ": la forma de decirle al compilador que realmente desea usar el resultado de una asignación es ser explícito y agregar paréntesis alrededor de la asignación. La declaración resultante tiene el mismo efecto, pero le dice al compilador que sabe lo que está haciendo. Se puede decir lo mismo sobre
foo/0.0: Ya que le está diciendo explícitamente al compilador "Esta es una división de punto flotante" usando en0.0lugar de0, el compilador confía en usted y no emitirá una advertencia.fuente
foo. Eso es intencional. Su afirmación solo es cierta en el caso de quefoosea un tipo de punto flotante.Esto parece un error de gcc, la documentación
-Wno-div-by-zerodice claramente :y después de las conversiones aritméticas habituales cubiertas en [expr.arith.conv] ambos operandos serán dobles :
y [expr.mul] :
Con respecto a si el punto flotante dividir por cero es un comportamiento indefinido y cómo lo maneja la implementación diferente, parece mi respuesta aquí . TL; DR; Parece que gcc se ajusta al Anexo F wrt para dividir el punto flotante por cero, por lo que undefined no juega un papel aquí. La respuesta sería diferente para clang.
fuente
La división de coma flotante por cero se comporta de manera diferente a la división entera por cero.
El estándar de coma flotante IEEE diferencia entre + inf y -inf, mientras que los enteros no pueden almacenar infinito. La división de enteros por resultado cero es un comportamiento indefinido. La división de coma flotante por cero está definida por el estándar de coma flotante y da como resultado + inf o -inf.
fuente