Как я могу преобразовать произвольный double в целое число, избегая неопределенного поведения?
Допустим, у меня есть функция, которая принимает 64-разрядное целое число, и я хочу вызвать ее сdouble
с произвольным числовым значением (т.е. оно может быть очень большим по величине или даже бесконечным):
void DoSomething(int64_t x);
double d = [...];
DoSomething(d);
Параграф 1 [conv.fpint] в стандарте C ++ 11 гласит:
Значение типа с плавающей запятой может быть преобразовано в значение типа целого числа. Конверсионные стволы; то есть дробная часть отбрасывается. Поведение не определено, если усеченное значение не может быть представлено в типе назначения.
Поэтому есть много значенийd
выше, это приведет к неопределенному поведению. Я бы хотел, чтобы преобразование было насыщенным, чтобы значения превышалиstd::numeric_limits<int64_t>::max()
(называетсяkint64max
ниже), включая бесконечность, становятся этим значением и аналогично минимальному представимому значению. Это кажется естественным подходом:
double clamped = std::min(d, static_cast<double>(kint64max));
clamped = std::max(clamped, static_cast<double>(kint64min));
DoSomething(clamped);
Но следующий абзац в стандарте говорит об этом:
Значение типа integer или типа перечисления с незаданной областью может быть преобразовано в значение типа с плавающей запятой. Результат точен, если это возможно. Если конвертируемое значение находится в диапазоне значений, которые могут быть представлены, но значение не может быть представлено точно, это определенный реализацией выбор следующего более низкого или более высокого представимого значения.
Такclamped
может все еще оказатьсяkint64max + 1
и поведение все еще может быть неопределенным.
Какой самый простой портативный способ сделать то, что я ищу? Бонусные баллы, если это также изящно обрабатываетNaN
s.
Обновить: Чтобы быть более точным, я хотел бы, чтобы все было верно дляint64_t SafeCast(double)
функция, которая решает эту проблему:
Для любого двойногоd
звонитSafeCast(d)
не выполняет неопределенное поведение в соответствии со стандартом, а также не создает исключение и не прерывает его иным образом.
Для любого двойногоd
В диапазоне[-2^63, 2^63)
, SafeCast(d) == static_cast<int64_t>(d)
, То есть,SafeCast
согласен с правилами преобразования C ++, где бы он ни был определен.
Для любого двойногоd >= 2^63
, SafeCast(d) == kint64max
.
Для любого двойногоd < -2^63
, SafeCast(d) == kint64min
.
Я подозреваю, что настоящая трудность заключается в том, чтобы выяснить,d
находится в диапазоне[-2^63, 2^63)
, Как обсуждалось в вопросе и в комментариях к другим ответам, я думаю, используяkint64max
вdouble
проверка на верхнюю границу не является началом из-за неопределенного поведения. Это может быть более перспективным для использованияstd::pow(2, 63)
, но я не знаю, гарантированно ли это будет точно 2 ^ 63.