Для некоторого целочисленного типа, как я могу найти значение, которое ближе всего к некоторому значению типа с плавающей точкой, даже если значение с плавающей запятой находится далеко за пределами отображаемого диапазона целого числа.
Или точнее:
Пусть 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 с плавающей запятой тип. Результат является точным, если это возможно. В противном случае это будет определенный реализацией выбор либо следующего более низкое или более высокое представляемое значение.