Как я могу преобразовать произвольный 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и поведение все еще может быть неопределенным.

Какой самый простой портативный способ сделать то, что я ищу? Бонусные баллы, если это также изящно обрабатываетNaNs.

Обновить: Чтобы быть более точным, я хотел бы, чтобы все было верно для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.

Ответы на вопрос(4)

Ваш ответ на вопрос