Caso 1:
#include <iostream>
int main()
{
double d = 15.50;
std::cout<<(d/0.0)<<std::endl;
}
Compila sem avisos e imprime inf
. OK, C ++ pode lidar com divisão por zero, ( veja ao vivo ).
Mas,
Caso 2:
#include <iostream>
int main()
{
double d = 15.50;
std::cout<<(d/0)<<std::endl;
}
O compilador dá o seguinte aviso ( veja ao vivo ):
warning: division by zero [-Wdiv-by-zero]
std::cout<<(d/0)<<std::endl;
Por que o compilador dá um aviso no segundo caso?
É 0 != 0.0
?
Editar:
#include <iostream>
int main()
{
if(0 == 0.0)
std::cout<<"Same"<<std::endl;
else
std::cout<<"Not same"<<std::endl;
}
resultado:
Same
c++
gcc
floating-point
divide-by-zero
Jayesh
fonte
fonte
Respostas:
A divisão de ponto flutuante por zero é bem definida pelo IEEE e dá infinito (positivo ou negativo de acordo com o valor do numerador (ou
NaN
para ± 0) ).Para inteiros, não há como representar o infinito e a linguagem define a operação como tendo um comportamento indefinido, portanto, o compilador tenta ajudar você a desviá-lo desse caminho.
No entanto, neste caso, uma vez que o numerador é um
double
, o divisor (0
) deve ser promovido a um duplo também e não há razão para dar um aviso aqui, embora não seja um aviso para,0.0
então acho que este é um bug do compilador.fonte
d/0
,0
é convertido para o tipo ded
.No C ++ padrão, ambos os casos são comportamento indefinido . Tudo pode acontecer, incluindo a formatação do seu disco rígido. Você não deve esperar ou confiar em "return inf. Ok" ou qualquer outro comportamento.
O compilador aparentemente decide dar um aviso em um caso e não no outro, mas isso não significa que um código está OK e o outro não. É apenas uma peculiaridade da geração de avisos do compilador.
Do padrão C ++ 17 [expr.mul] / 4:
fonte
std::numeric_limits<T>::is_iec559
fortrue
, então a divisão por zero paraT
não é UB (e na maioria das plataformas étrue
paradouble
efloat
, embora seja portátil, você precisa verificar isso explicitamente com umif
ouif constexpr
).is_iec559
comotrue
significa que a implementação está documentando o comportamento que o padrão deixa indefinido. É apenas que este é um caso em que a documentação da implementação pode ser lida programaticamente. Nem mesmo o único: o mesmo se aplica ais_modulo
tipos inteiros com sinal.Meu melhor palpite para responder a essa pergunta em particular seria que o compilador emite um aviso antes de realizar a conversão de
int
paradouble
.Então, as etapas seriam assim:
/(T, T2)
, ondeT=double
,T2=int
.std::is_integral<T2>::value
étrue
eb == 0
- isso dispara um aviso.T2
paradouble
É claro que isso é especulação e se baseia em especificações definidas pelo compilador. Do ponto de vista padrão, estamos lidando com possíveis comportamentos indefinidos.
Observe que este é o comportamento esperado de acordo com a documentação do GCC
(aliás, parece que este sinalizador não pode ser usado explicitamente no GCC 8.1)
fonte
/
para saber que é divisão. Se o lado esquerdo fosse umFoo
objeto e houvesse umoperator/(Foo, int)
, então poderia nem mesmo haver divisão. O compilador só sabe sua divisão quando escolheubuilt-in / (double, double)
usar uma conversão implícita do lado direito. Mas isso significa que NÃO está fazendo uma divisão porint(0)
, está fazendo uma divisão pordouble(0)
.operator /(double, int)
certamente é aceitável. Em seguida, ele diz que a conversão é realizada antes de qualquer outra ação, mas o GCC poderia fazer uma verificação rápida seT2
for do tipo inteirob == 0
e emitir um aviso em caso afirmativo. Não tenho certeza se isso é totalmente compatível com o padrão, mas os compiladores têm total liberdade para definir os avisos e quando eles devem ser disparados.operator/(double,int)
realmente existe. O compilador pode, por exemplo, decidir otimizara/b
a constanteb
substituindo-a pora * (1/b)
. Claro, isso significa que você não está mais chamandooperator/(double,double)
em tempo de execução, mas o mais rápidooperator*(double,double)
. Mas agora é o otimizador que tropeça1/0
, a constante para a qual ele teria que alimentaroperator*
Não vou entrar no desastre UB / não UB nesta resposta.
Eu só quero apontar isso
0
e0.0
são diferentes apesar de0 == 0.0
avaliarem como verdade.0
é umint
literal e0.0
é umdouble
literal.No entanto, neste caso, o resultado final é o mesmo:
d/0
é uma divisão em ponto flutuante porqued
é dupla e, portanto,0
está implicitamente convertida em dupla.fonte
double
por umint
significa para o qual oint
é convertidodouble
e é especificado no padrão que0
converte para0.0
(conv.fpint / 2)0
é o mesmo que0.0
0 != 0.0
?". OP nunca pergunta se eles são "iguais". Também me parece que a intenção da pergunta é sed/0
pode se comportar de maneira diferente ded/0.0
Eu argumentaria isso
foo/0
e nãofoo/0.0
são os mesmos. Ou seja, o efeito resultante da primeira (divisão inteira ou divisão de ponto flutuante) é altamente dependente do tipo de , enquanto o mesmo não é verdade para a segunda (será sempre uma divisão de ponto flutuante).foo
Se algum dos dois é UB é irrelevante. Citando o padrão:
(Ênfase minha)
Considere o aviso " sugira parênteses em torno da atribuição usada como valor verdade ": a maneira de dizer ao compilador que você realmente deseja usar o resultado de uma atribuição é sendo explícito e adicionando parênteses ao redor da atribuição. A instrução resultante tem o mesmo efeito, mas informa ao compilador que você sabe o que está fazendo. O mesmo pode ser dito sobre
foo/0.0
: Como você está explicitamente dizendo ao compilador "Esta é uma divisão de ponto flutuante" usando em0.0
vez de0
, o compilador confia em você e não emitirá um aviso.fonte
foo
. Isso é intencional. Sua afirmação só é verdadeira no caso defoo
ser um tipo de ponto flutuante.Parece um bug do gcc, a documentação
-Wno-div-by-zero
diz claramente :e após as conversões aritméticas usuais abordadas em [expr.arith.conv], ambos os operandos serão duplos :
e [expr.mul] :
Com relação a se a divisão de ponto flutuante por zero é um comportamento indefinido e como diferentes implementações lidam com isso, parece minha resposta aqui . TL; DR; Parece que o gcc está em conformidade com o Anexo F em relação ao ponto flutuante dividido por zero, portanto, undefined não desempenha um papel aqui. A resposta seria diferente para clang.
fonte
A divisão de ponto flutuante por zero se comporta de maneira diferente da divisão de inteiro por zero.
O padrão de ponto flutuante IEEE diferencia entre + inf e -inf, enquanto os inteiros não podem armazenar o infinito. A divisão inteira por resultado zero é um comportamento indefinido. A divisão de ponto flutuante por zero é definida pelo padrão de ponto flutuante e resulta em + inf ou -inf.
fonte