Для некоторого целочисленного типа, как я могу найти значение, которое ближе всего к некоторому значению типа с плавающей точкой, даже если значение с плавающей запятой находится далеко за пределами отображаемого диапазона целого числа.
Или точнее:
Пусть F
- тип с плавающей запятой (возможно, float
, double
или long double
).
Пусть I
- целочисленный тип.
Предположим, что оба F
и I
имеют действительные специализации std::numeric_limits<>
.
Учитывая представимое значение F
и используя только С++ 03, как я могу найти ближайшее представляемое значение I
?
Я получаю в чистом, эффективном и потокобезопасном решении, и тот, который не принимает ничего о платформе, кроме того, что гарантируется С++ 03.
Если такого решения не существует, можно ли найти его, используя новые функции C99/С++ 11?
Использование lround()
для C99 кажется проблематичным из-за нетривиального способа регистрации ошибок домена. Могут ли эти ошибки домена быть обнаружены в переносном и потокобезопасном режиме?
Примечание. Я знаю, что Boost, вероятно, предлагает решение через его шаблон boost::numerics::converter<>
, но из-за его высокой сложности и многословия, и я не смог извлечь из него необходимые вещи, и поэтому я не смог чтобы проверить, делает ли их решение допущения за пределами С++ 03.
Следующий наивный подход терпит неудачу из-за того, что результат I(f)
равен undefined С++ 03, когда неотъемлемая часть F
не является представимым значением I
.
template<class I, class F> I closest_int(F f)
{
return I(f);
}
Рассмотрим следующий подход:
template<class I, class F> I closest_int(F f)
{
if (f < std::numeric_limits<I>::min()) return std::numeric_limits<I>::min();
if (std::numeric_limits<I>::max() < f) return std::numeric_limits<I>::max();
return I(f);
}
Это также терпит неудачу, поскольку интегральные части F(std::numeric_limits<I>::min())
и F(std::numeric_limits<I>::max())
все еще не могут быть представлены в I
.
Наконец, рассмотрим этот третий подход, который также терпит неудачу:
template<class I, class F> I closest_int(F f)
{
if (f <= std::numeric_limits<I>::min()) return std::numeric_limits<I>::min();
if (std::numeric_limits<I>::max() <= f) return std::numeric_limits<I>::max();
return I(f);
}
На этот раз I(f)
всегда будет иметь четко определенный результат, так как F(std::numeric_limits<I>::max())
может быть намного меньше, чем std::numeric_limits<I>::max()
, возможно, мы вернем std::numeric_limits<I>::max()
для значения с плавающей запятой, которое несколько целых значений ниже std::numeric_limits<I>::max()
.
Обратите внимание, что вся проблема возникает из-за того, что она undefined округляется ли конверсия F(i)
или до ближайшего представляемого значения с плавающей запятой.
Вот соответствующий раздел из С++ 03 (4.9 Плавающие интегральные преобразования):
Значение целочисленного типа или типа перечисления может быть преобразовано в значение r с плавающей запятой тип. Результат является точным, если это возможно. В противном случае это будет определенный реализацией выбор либо следующего более низкое или более высокое представляемое значение.