Почему в C ++ отличается static_cast <unsigned> от отрицательных чисел, если число постоянное или нет

28

Какие правила С ++ означают, что равно равно ложно ? Данный:

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

Например, 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;
}

Производит следующий вывод:

 f=ffffffff
cf=0
GreyMattR
источник
6
Имейте в виду: вы были пойманы часто забытым правилом о неопределенном поведении!
Вирсавия
Каких результатов вы ожидаете, преобразовав отрицательное число с плавающей точкой в ​​беззнаковое?
Амадеус
1
@ Amadeus, вероятно, обычная обертка, которую мы получаем при преобразовании отрицательного целого числа. Я должен был проверить, что это был UB, потому что это удивило меня.
AProgrammer
1
@ Амадеус, это был скорее случай понимания разницы. Я исправил ошибку опечатки несколько недель назад ... const-float был явно приведен к unsigned (ошибка) и неявно обратно к подписанному (как параметр функции sign). Позже я подумал, почему исходная ошибка приводила к нулевому значению в функции. Тестирование показывает, что это произошло потому, что поплавок был постоянным. Неконстантный float, который был явно приведен к unsigned, а затем неявно приведен к подписанному, не привел к тому же поведению - двойное приведение non-const имело исходное и ожидаемое значение.
GreyMattR

Ответы:

26

Поведение вашей программы не определено : стандарт C ++ не определяет преобразование отрицательного типа с плавающей запятой в unsignedтип.

(Обратите внимание, что знакомое поведение обтекания применимо только к отрицательным целочисленным типам.)

Поэтому нет смысла пытаться объяснить результаты вашей программы.

Вирсавия
источник
1
Определено ли это, если бы я вместо этого конвертировал float-> int-> unsigned?
Иксисарвинен
5
@ Yksisarvinen: только если floatнаходится в пределах диапазона int.
Вирсавия
Я принимаю, что UB является правильным ответом, и так должно быть до конца ... но учитывая это ... Каков вероятный ответ автора-компилятора, который объясняет, почему все компиляторы в Compiler Explorer (clang / gcc / djgpp) производят эквивалентный (UB) выход?
GreyMattR
5
@GreyMattR Если компилятор может доказать, что значение гарантированно будет отрицательным во время приведения, то он может оставить результат приведения неинициализированным, или установить его равным нулю, или что-либо еще, что он хочет сделать. Если компилятор не может доказать это, он должен сгенерировать код для выполнения приведения. Для таких целей он может повторно использовать код для приведения к целочисленному типу со знаком (результат будет «неправильным», только если приведен тип UB, что означает, что на самом деле это не так). При более агрессивной оптимизации бросок также не будет генерироваться в неконстантном случае.
Брайан
@ Брайан, спасибо за это полезное объяснение.
GreyMattR