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