Выполняет ли литье в int после std:: floor гарантированный правильный результат?
Мне нужна функция floor
с синтаксисом
int floor(double x);
но std::floor
возвращает a double
. Является
static_cast <int> (std::floor(x));
гарантированно даст мне правильное целое число, или я могу решить проблему отдельно? Кажется, это работает, но я хотел бы точно знать.
Для бонусных очков, почему черт std::floor
возвращает a double
в первую очередь?
Ответы
Ответ 1
Диапазон double больше, чем диапазон 32 или 64 битных целых чисел, поэтому std::floor
возвращает double
. Кастинг в int
должен быть точным до тех пор, пока он находится в соответствующем диапазоне, но имейте в виду, что double
не может точно представлять все 64-битные целые числа, поэтому вы также можете столкнуться с ошибками, когда вы выходите за пределы точки точность которого double
такова, что разность между двумя последовательными удвоениями больше 1.
Ответ 2
static_cast <int> (std::floor(x));
делает в значительной степени то, что вы хотите, да. Он дает вам ближайшее целое число, округленное к -infinity. По крайней мере, пока ваш вход находится в диапазоне, представляемом ints.
Я не уверен, что вы подразумеваете под "добавлением .5 и еще чего-то, но он не будет иметь такого же эффекта
И std:: floor возвращает double, потому что это самый общий. Иногда вам может понадобиться округлить float или double, но сохранить тип. То есть, от 1.3f до 1.0f, а не до 1.
Это было бы трудно сделать, если std:: floor возвратил int. (или, по крайней мере, у вас будет лишний ненужный бросок, замедляющий работу).
Если пол выполняет только округление, не изменяя тип, вы можете применить его к int, если вам нужно.
Другая причина заключается в том, что диапазон удвоений намного больше, чем у ints. Возможно, не удастся обойти все двойники до целых чисел.
Ответ 3
В стандарте С++ говорится (4.9.1):
"rvalue типа с плавающей точкой может быть преобразовано в rvalue целочисленного типа. Преобразование обрезается, то есть дробная часть отбрасывается. Поведение undefined, если усеченное значение не может быть представлено в тип назначения".
Итак, если вы конвертируете double в int, число находится в пределах интервала int, а требуемое округление - к нулю, тогда достаточно просто указать число в int:
(целое) х;
Ответ 4
Если вы хотите иметь дело с различными числовыми условиями и хотите обрабатывать различные типы конверсий контролируемым образом, то, возможно, вам стоит взглянуть на 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;
}
Ответ 5
Большая часть стандартной математической библиотеки использует удвоение, но также предоставляет версии с плавающей точкой. std:: floorf() - это единственная версия версии std:: floor(), если вы предпочитаете не использовать удвоения.
Изменить: я удалил часть своего предыдущего ответа. Я сказал, что пол был излишним при бросании в int, но я забыл, что это верно только для положительных чисел с плавающей точкой.