Como @Angew apontou , o !=operador precisa do mesmo tipo em ambos os lados.
(float)i != iresulta na promoção do RHS para flutuar também, então fizemos (float)i != (float)i.
g ++ também gera um loop infinito, mas não otimiza o trabalho de dentro dele. Você pode ver que ele converte int-> float com cvtsi2sse faz ucomiss xmm0,xmm0para comparar (float)iconsigo mesmo. (Essa foi sua primeira pista de que seu código-fonte C ++ não significa o que você achou que ele gostava de @resposta de Angew explica.)
x != xsó é verdadeiro quando está "desordenado" porque xera NaN. ( INFINITYcompara igual a si mesmo na matemática IEEE, mas NaN não. NAN == NANé falso, NAN != NANé verdadeiro).
gcc7.4 e mais antigos otimizam corretamente seu código jnpcomo o ramo do loop ( https://godbolt.org/z/fyOhW1 ): mantenha o loop enquanto os operandos x != x não forem NaN. (gcc8 e posterior também verifica jese há uma quebra do loop, deixando de otimizar com base no fato de que sempre será verdadeiro para qualquer entrada não NaN). x86 FP compara PF definido em não ordenado.
E, a propósito, isso significa que a otimização do clang também é segura : ele apenas precisa CSE (float)i != (implicit conversion to float)icomo sendo o mesmo, e provar que i -> floatnunca é NaN para o intervalo possível de int.
(Embora dado que este loop atingirá UB de estouro assinado, é permitido emitir literalmente qualquer asm que desejar, incluindo uma ud2instrução ilegal ou um loop infinito vazio, independentemente do que o corpo do loop realmente era.) Mas ignorando o UB de estouro assinado , essa otimização ainda é 100% legal.
O GCC falha em otimizar o corpo do loop, mesmo -fwrapvpara tornar o estouro de inteiro com sinal bem definido (como complemento de 2). https://godbolt.org/z/t9A8t_
Mesmo habilitando -fno-trapping-mathnão ajuda. (O padrão do GCC infelizmente é habilitar
-ftrapping-mathmesmo que a implementação do GCC esteja quebrada / com erros .) Int-> conversão float pode causar uma exceção inexata de FP (para números muito grandes para serem representados exatamente), então, com exceções possivelmente desmascaradas, é razoável não otimize o corpo do loop. (Porque a conversão 16777217para float pode ter um efeito colateral observável se a exceção inexata for desmascarada.)
Mas com -O3 -fwrapv -fno-trapping-math, é 100% de otimização perdida não compilar isso em um loop infinito vazio. Sem #pragma STDC FENV_ACCESS ON, o estado dos sinalizadores fixos que registram exceções de FP mascaradas não é um efeito colateral observável do código. Não int-> floatconversão pode resultar em NaN, então x != xnão pode ser verdade.
Todos esses compiladores são otimizados para implementações C ++ que usam IEEE 754 de precisão única (binary32) floate 32 bits int.
O loop corrigido de bug(int)(float)i != i teria UB em implementações C ++ com estreito de 16 bits inte / ou mais amplo float, porque você atingiu o UB de estouro de inteiro com sinal antes de atingir o primeiro inteiro que não era exatamente representável como a float.
Mas o UB sob um conjunto diferente de opções definidas pela implementação não tem consequências negativas ao compilar para uma implementação como gcc ou clang com o x86-64 System V ABI.
BTW, você poderia calcular estaticamente o resultado deste loop de FLT_RADIXe FLT_MANT_DIG, definido em <climits>. Ou pelo menos você pode em teoria, se floatrealmente se encaixa no modelo de um float IEEE em vez de algum outro tipo de representação de número real como um Posit / unum.
Não tenho certeza de quanto o padrão ISO C ++ esclarece sobre floatcomportamento e se um formato que não fosse baseado em expoentes de largura fixa e campos de significand seria compatível com os padrões.
Nos comentários:
@geza, gostaria de saber o número resultante!
@nada: é 16777216
Você está afirmando que tem este loop para imprimir / devolver 16777216 ?
Atualização: como esse comentário foi excluído, acho que não. Provavelmente, o OP está apenas citando o floatantes do primeiro inteiro que não pode ser representado exatamente como 32 bits float. https://en.wikipedia.org/wiki/Single-precision_floating-point_format#Precision_limits_on_integer_values ou seja, o que eles esperavam verificar com este código bugado.
A versão corrigida do bug, é claro 16777217, imprimir , o primeiro inteiro que não exatamente representável, em vez do valor anterior.
(Todos os valores flutuantes mais altos são inteiros exatos, mas são múltiplos de 2, depois 4, depois 8, etc. para valores de expoentes maiores que a largura do significante. Muitos valores inteiros maiores podem ser representados, mas 1 unidade no último lugar (do significando) é maior que 1, portanto não são inteiros contíguos. O maior finito floatestá logo abaixo de 2 ^ 128, o que é muito grande para mesmo int64_t.)
Se algum compilador sair do loop original e imprimi-lo, será um bug do compilador.