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
NaN
para ± 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.0
así que creo que esto es un error del compilador.fuente
d/0
,0
se 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_iec559
estrue
, entonces la división por ceroT
no es UB (y en la mayoría de las plataformas estrue
paradouble
yfloat
, aunque para ser portátil, es necesario comprobarlo explícitamente conif
oif constexpr
).is_iec559
comotrue
significa 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_modulo
tipos 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
int
adouble
.Entonces, los pasos serían así:
/(T, T2)
, dondeT=double
,T2=int
.std::is_integral<T2>::value
estrue
yb == 0
- esto activa una advertencia.T2
adouble
Por 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 unFoo
objeto, 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 siT2
es de tipo enterob == 0
y 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/b
para constanteb
reemplazá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
0
y0.0
somos diferentes a pesar de0 == 0.0
evaluarlos como verdaderos.0
es unint
literal y0.0
es undouble
literal.Sin embargo, en este caso, el resultado final es el mismo:
d/0
es una división de punto flotante porqued
es doble y, por0
lo tanto, se convierte implícitamente en doble.fuente
double
por unint
medio al queint
se conviertedouble
, y se especifica en el estándar que0
convierte a0.0
(conv.fpint / 2)0
es lo mismo que0.0
0 != 0.0
?". OP nunca pregunta si son "iguales". También me parece que la intención de la pregunta es si sed/0
puede comportar de manera diferente ad/0.0
Yo diría que
foo/0
y nofoo/0.0
son 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.0
lugar 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 quefoo
sea un tipo de punto flotante.Esto parece un error de gcc, la documentación
-Wno-div-by-zero
dice 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