Dlaczego w C ++ różnice między liczbami ujemnymi static acast <unsigned> są różne, jeśli liczba jest stała, czy nie

28

Jakie reguły C ++, które oznaczają równość,fałszywe ? Dany:

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

Np. 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;
}

Daje następujące dane wyjściowe:

 f=ffffffff
cf=0
GreyMattR
źródło
6
Wyraź opinię: złapała Cię często zapomniana zasada dotycząca nieokreślonego zachowania!
Batszeba
Jakich wyników oczekujesz, przekształcając liczbę zmiennoprzecinkową na niepodpisaną?
Amadeus
1
@Amadeus prawdopodobnie zwykle zawija się podczas konwersji ujemnej liczby całkowitej. Musiałem sprawdzić, czy to UB, ponieważ mnie to zaskoczyło.
AProgrammer
1
@Amadeus, bardziej chodziło o zrozumienie różnicy. Naprawiłem błąd literowy kilka tygodni temu ... const-float został jawnie przekazany do unsigned (błąd) i niejawnie z powrotem do podpisanego (jako parametr funkcji podpisanej). Później zastanawiałem się, dlaczego oryginalny błąd powodował zerową wartość funkcji. Testy sugerują, że było tak, ponieważ zmiennoprzecinkowe było const. Niestabilna liczba zmiennoprzecinkowa, która została jawnie rzutowana na niepodpisaną, a następnie domyślnie rzutowana z powrotem na podpisaną, nie spowodowała takiego samego zachowania - podwójnie rzutowany non-const miał oryginalną i oczekiwaną wartość.
GreyMattR

Odpowiedzi:

26

Zachowanie Twojego programu jest niezdefiniowane : standard C ++ nie definiuje konwersji ujemnego typu zmiennoprzecinkowego na unsignedtyp.

(Uwaga: znane zachowanie zawijania dotyczy tylko typów całek ujemnych ).

Dlatego nie ma sensu wyjaśniać wyników programu.

Batszeba
źródło
1
Czy jest to zdefiniowane, jeśli zamiast tego dokonam konwersji float-> int-> unsigned?
Yksisarvinen
5
@Yksisarvinen: Tylko jeśli floatjest w zakresie int.
Batszeba
Akceptuję, że UB jest poprawną odpowiedzią, a więc powinien być jej koniec ... ale biorąc pod uwagę, że ... Jaka jest prawdopodobna odpowiedź kompilatora-autora, która wyjaśnia, dlaczego wszystkie kompilatory w Compiler Explorer (clang / gcc / djgpp) produkują równoważne wyjście (UB)?
GreyMattR,
5
@GreyMattR Jeśli kompilator może udowodnić, że wartość jest ujemna w momencie rzutowania, może pozostawić wynik rzutowania niezainicjowany lub ustawić go na zero lub cokolwiek innego, co chce zrobić. Jeśli kompilator nie może tego udowodnić, musi wygenerować kod, aby wykonać rzutowanie. Do takich celów może ponownie użyć kodu do rzutowania na typ całkowity ze znakiem (wynik będzie „zły”, jeśli rzutowanie to UB, co oznacza, że ​​tak naprawdę nie jest zły). Przy bardziej agresywnej optymalizacji obsada nie będzie emitowana również w przypadku non-const.
Brian,
@Brian, dzięki za pomocne wyjaśnienie.
GreyMattR,