Por que em C ++ o static_cast <não assinado> de números negativos difere se o número é constante ou não

28

Quais são as regras do C ++ que significam igual é falso ? Dado:

float f {-1.0};
bool equal = (static_cast<unsigned>(f) == static_cast<unsigned>(-1.0));

Por exemplo, https://godbolt.org/z/fcmx2P

#include <iostream>

int main() 
{
          float   f {-1.0};
    const float  cf {-1.0};

    std::cout << std::hex;
    std::cout << " f" << "=" << static_cast<unsigned>(f) << '\n';
    std::cout << "cf" << "=" << static_cast<unsigned>(cf) << '\n';

    return 0;
}

Produz a seguinte saída:

 f=ffffffff
cf=0
GreyMattR
fonte
6
Tenha um voto positivo: você foi pego por uma regra muitas vezes esquecida sobre comportamento indefinido!
Bathsheba
Quais resultados você espera converter um float negativo em um não assinado?
Amadeus
11
@Amadeus provavelmente é o que de costume temos ao converter um número inteiro negativo. Eu tive que verificar se era UB porque isso me surpreendeu.
AProgrammer 18/11/19
11
@Amadeus, foi mais um caso de entender a diferença. Corrigi um erro de digitação há algumas semanas atrás ... um const-float foi explicitamente convertido em sem sinal (o bug) e implicitamente de volta ao assinado (como um parâmetro de função assinado). Mais tarde, ponderei por que o bug original estava causando um valor zero na função. Os testes sugerem que foi porque o flutuador era const. Um float não const que foi explicitamente convertido para não assinado e, em seguida, convertido implicitamente de volta para assinado não resultou no mesmo comportamento - o non-const convertido duas vezes tinha o valor original e esperado.
GreyMattR

Respostas:

26

O comportamento do seu programa é indefinido : o padrão C ++ não define a conversão de um tipo de ponto flutuante negativo em um unsignedtipo.

(Observe que o comportamento familiar envolvente se aplica apenas a tipos integrais negativos .)

Portanto, há pouco sentido em tentar explicar a saída do seu programa.

Bathsheba
fonte
11
Está definido se eu deveria converter float-> int-> unsigned?
Yksisarvinen 18/11/19
5
@Yksisarvinen: Somente se o floatestiver dentro do intervalo de um int.
Bathsheba
Eu aceito que UB é a resposta correta e, portanto, deve ser o fim dela ... mas, considerando isso ... Qual é a resposta provável do compilador-gravador que explica por que todos os compiladores no Compiler Explorer (clang / gcc / djgpp) produzem a saída equivalente (UB)?
GreyMattR
5
@GreyMattR Se o compilador puder provar que o valor é garantido como negativo no momento do lançamento, ele poderá deixar o resultado do lançamento não inicializado ou defini-lo como zero, ou o que mais ele desejar fazer. Se o compilador não puder provar isso, ele precisará gerar código para executar a conversão. Para tais fins, ele pode reutilizar o código para converter para o tipo inteiro assinado (o resultado só será "errado" se a conversão for UB, o que significa que não está realmente errado). Com uma otimização mais agressiva, o elenco também não será emitido no caso não const.
18719 Brian
@ Brian, obrigado por essa explicação útil.
GreyMattR