Очевидное несоответствие во время приведения типов в C ++ (и C) - PullRequest
3 голосов
/ 15 февраля 2020

Случайно кто-нибудь может объяснить вывод здесь? Я пытался понять, почему приведение типа литерала приводит к результату, отличному от преобразования типа переменной в этом коде.

#include <iostream>
#include <cstdint>

using namespace std;

int main()
{
    double intermediate;
    intermediate = -1.0;
    cout << "intermediate = " << intermediate << endl;
    cout << "uint64_t(intermediate) = " << uint64_t(intermediate) << endl;
    cout << "uint64_t((double)(-1)) = " << uint64_t((double)(-1)) << endl;

    return 0;
}

Вывод, который я получаю:

intermediate = -1                                                                                                                                   
uint64_t(intermediate) = 18446744073709551615                                                                                                       
uint64_t((double)(-1)) = 0  

1 Ответ

5 голосов
/ 15 февраля 2020

Вы выполняете преобразование вне диапазона от типа с плавающей запятой к целочисленному типу.

Поскольку двойное значение -1.0 не может вписаться в uint64_t, это считается недействительным преобразование дальности. Такое преобразование вызывает неопределенное поведение . Это означает, среди прочего, что две попытки преобразования не должны давать одинаковые результаты.

Обратите внимание, что это отличается от целого числа со знаком в целое число без знака, которое хорошо определено во всех случаях.

Это продиктовано 7.10p1 C ++ 17 стандарта :

Значение типа с плавающей запятой может быть преобразовано в значение типа целого числа. Преобразование усекается, то есть дробная часть отбрасывается. Поведение не определено, если усеченное значение не может быть представлено в типе назначения.

Раздел 6.3.1.4p1 C11 стандарта имеет аналогичный язык:

Когда конечное значение реального плавающего типа преобразуется в целочисленный тип, отличный от _Bool, дробная часть отбрасывается (т. Е. Значение усекается до нуля). Если значение интегральной части не может быть представлено целочисленным типом, поведение не определено. 61)

...

61) Операция переопределения, выполняемая, когда значение целочисленного типа преобразуется в тип без знака, не должна выполняться, когда значение реального плавающего значения Тип преобразуется в тип без знака. Таким образом, диапазон переносимых вещественных плавающих значений составляет (−1, Utype_MAX + 1)

. Чтобы получить согласованный результат, сначала необходимо привести к целочисленному типу со знаком, а затем к типу без знака.

cout << "uint64_t(intermediate) = " << 
        static_cast<uint64_t>(static_cast<int>(intermediate)) << endl;

Для C:

printf("uint64_t(intermediate) = %llu\n", (uint64_t)(int)intermediate);
...