Гарантирует ли приведение к int после std :: floor правильный результат? - PullRequest
37 голосов
/ 03 марта 2009

Я бы хотел floor функцию с синтаксисом

int floor(double x);

но std::floor возвращает double. Есть

static_cast <int> (std::floor(x));

гарантированно даст мне правильное целое число, или у меня может быть проблема "один за другим"? Кажется, это работает, но я хотел бы знать наверняка.

Для бонусных баллов, почему, черт возьми, std::floor возвращает double на первом месте?

Ответы [ 5 ]

28 голосов
/ 03 марта 2009

Диапазон double намного больше, чем диапазон 32 или 64-битных целых, поэтому std::floor возвращает double. Приведение к int должно быть в порядке, пока оно находится в соответствующем диапазоне - но имейте в виду, что double не может точно представлять все 64-битные целые числа, поэтому вы можете также получить ошибки, если выйдете за пределы точки в точность которого double такова, что разница между двумя последовательными двойными значениями больше 1.

15 голосов
/ 03 марта 2009
static_cast <int> (std::floor(x));

делает в значительной степени то, что вы хотите, да. Это дает вам ближайшее целое число, округленное до -infinity. По крайней мере, пока ваш ввод находится в диапазоне, представленном целыми числами. Я не уверен, что вы имеете в виду, добавляя .5 и еще много чего, но это не даст того же эффекта

И std :: floor возвращает double, потому что это самое общее. Иногда вы можете захотеть округлить число с плавающей запятой или удвоить, но сохранить тип. То есть, от 1,3f до 1,0f, а не до 1.

Это было бы трудно сделать, если бы std :: floor вернул int. (или, по крайней мере, у вас будет дополнительный ненужный состав, замедляющий процесс).

Если floor выполняет округление только без изменения типа, вы можете привести его к int, если / когда вам нужно.

Другая причина в том, что диапазон двойников намного больше, чем у целых. Может оказаться невозможным округлить все двойные числа до целых.

7 голосов
/ 26 мая 2009

Стандарт C ++ гласит (4.9.1):

"Значение r типа с плавающей запятой может быть преобразовано в значение r целого типа. Преобразование усекается; то есть дробная часть отбрасывается. Поведение не определено, если усеченное значение не может быть представлено в типе назначения ».

Так что, если вы конвертируете double в целое число, число находится в диапазоне от int и требуемое округление до нуля, то достаточно просто привести число к int:

(интермедиат) х;

5 голосов
/ 03 марта 2009

Если вы хотите иметь дело с различными числовыми условиями и хотите обрабатывать различные типы преобразований контролируемым образом, то, возможно, вам следует взглянуть на Boost.NumericConversion . Эта библиотека позволяет обрабатывать странные случаи (например, вне диапазона, округления, диапазонов и т. Д.)

Вот пример из документации:

#include <cassert>
#include <boost/numeric/conversion/converter.hpp>

int main() {

    typedef boost::numeric::converter<int,double> Double2Int ;

    int x = Double2Int::convert(2.0);
    assert ( x == 2 );

    int y = Double2Int()(3.14); // As a function object.
    assert ( y == 3 ) ; // The default rounding is trunc.

    try
    {
        double m = boost::numeric::bounds<double>::highest();
        int z = Double2Int::convert(m); // By default throws positive_overflow()
    }
    catch ( boost::numeric::positive_overflow const& )
    {
    }

    return 0;
}
2 голосов
/ 03 марта 2009

Большая часть стандартной математической библиотеки использует double, но также предоставляет и плавающие версии. std :: floorf () - это версия std :: floor () с одинарной точностью, если вы предпочитаете не использовать double.

Редактировать: я удалил часть моего предыдущего ответа. Я заявлял, что при приведении к int слово было избыточным, но я забыл, что это верно только для положительных чисел с плавающей запятой.

...