Моя задача — округлить float до int.
Вопрос: можно ли для этого использовать std::round?
И если да, то как правильно избавится от предупреждения: warning C4244: '=': conversion from 'float' to 'int', possible loss of data.
Здравствуйте, B0FEE664, Вы писали:
BFE>Моя задача — округлить float до int. BFE>Вопрос: можно ли для этого использовать std::round? BFE>И если да, то как правильно избавится от предупреждения: warning C4244: '=': conversion from 'float' to 'int', possible loss of data.
Здесь фишка в том, что у std::round тип возвращамого значения тот же, что и у параметра. Поэтому явный каст к нужному типу вполне приемлем, я считаю:
Здравствуйте, B0FEE664, Вы писали:
BFE>Моя задача — округлить float до int. BFE>Вопрос: можно ли для этого использовать std::round? BFE>И если да, то как правильно избавится от предупреждения: warning C4244: '=': conversion from 'float' to 'int', possible loss of data.
я обычно так делаю:
double d = 5.5;
int i = (int)(d+0.5);
для отрицательных надо отнять, если хочешь округление к большему по модулю
Здравствуйте, rg45, Вы писали:
R>Здесь фишка в том, что у std::round тип возвращамого значения тот же, что и у параметра. Поэтому явный каст к нужному типу вполне приемлем, я считаю: R>
R>int value = int(std::round(3.14f));
R>
А гарантия того, что в value будет 3, а не 2 есть?
Здравствуйте, B0FEE664, Вы писали:
BFE>Почему не используете round ?
1) Привычка — в старом стандарте его не было, да я пока не перешел на него. Домашнее попиливаю на старом, а на работе 11ый использую, но мало — там под железяки пишем, не особо тоже нужен
2) Запомнить кто что делает round, floor etc — еще в паскале не мог Но там каститься не так вроде просто, проще было таки через эти функции округлять
Кстати, тебе, чтобы самому не кастить результат, лучше использовать lround/llround — они long/long long возвращают
Здравствуйте, rg45, Вы писали:
R>Можно попробовать в цикле прогнать через весть диапазон int.
Наверно хотел написать через весь диапазон float/double ?
Здравствуйте, pagid, Вы писали:
R>>Можно попробовать в цикле прогнать через весть диапазон int. P>Наверно хотел написать через весь диапазон float/double ?
Мы же не ставим под сомнение правильность работы std::round? Под вопросом лишь не возникнет ли ошибки при преобразовании результата этой функции к целому числу.
Здравствуйте, B0FEE664, Вы писали:
R>>Здесь фишка в том, что у std::round тип возвращамого значения тот же, что и у параметра. Поэтому явный каст к нужному типу вполне приемлем, я считаю: R>>
R>>int value = int(std::round(3.14f));
R>>
BFE>А гарантия того, что в value будет 3, а не 2 есть?
Только для относительно небольших чисел. (1ull << std::numeric_limits<Real>::digits) + 1 — первое целое число, которое не имеет точного представления в вещественном типе Real. К этому и подобным ему числам значения вещественного типа не смогут округлиться.
Для float число значащих цифр мантиссы равно 23 + 1 = 24, поэтому пограничное "хорошее" число (которым завершается непрерывная последовательность целых чисел, представимых типом float) будет равно 16.777.216.
Для double число значащих цифр в мантиссе — 52 + 1 = 53, пограничное число — 9.007.199.254.740.992.
Поэтому, если хочется абсолютно надежного округления, лучше написать обертку над std::round, которая бы проводила необходимую проверку возможности корректного округления (эту обертку можно сделать шаблонной, параметризовав целочисленным типом, к которому должно быть приведен результат выполнения std::round).
Здравствуйте, Constructor, Вы писали:
R>>>Здесь фишка в том, что у std::round тип возвращамого значения тот же, что и у параметра. Поэтому явный каст к нужному типу вполне приемлем, я считаю: R>>>
R>>>int value = int(std::round(3.14f));
R>>>
BFE>>А гарантия того, что в value будет 3, а не 2 есть?
C>Только для относительно небольших чисел. (1ull << std::numeric_limits<Real>::digits) + 1 — первое целое число, которое не имеет точного представления в вещественном типе Real. К этому и подобным ему числам значения вещественного типа не смогут округлиться.
C>Для float число значащих цифр мантиссы равно 23 + 1 = 24, поэтому пограничное "хорошее" число (которым завершается непрерывная последовательность целых чисел, представимых типом float) будет равно 16.777.216.
Эти рассуждения, конечно же верны и на их основании мы можем сделать вывод, что не всякое целое может быть представлено в виде float. Другими словами, слишком большая потеря точности может возникнуть при преобразовании int -> float. Но вопрос ведь сейчас стоит об обратном преобразовании: float -> int: может ли случиться так, что при преобразовании результа выражениия std::round(f) к целому числу, будет выбрано число, не самое близкое к результату выражения? (Где f имеет тип float и находится в диапазоне [std::numeric_limits<int>::min(), std::numeric_limits<int>::max()])
Здравствуйте, rg45, Вы писали:
R>Эти рассуждения, конечно же верны и на их основании мы можем сделать вывод, что не всякое целое может быть представлено в виде float. Другими словами, слишком большая потеря точности может возникнуть при преобразовании int -> float.
Точно! Почему-то вдруг взбрело в голову, что int → float как-то может повлиять на float → int.
R>Вопрос же сейчас стоит об обратном преобразовании: float -> int: может ли случиться так, что при преобразовании результа выражениия std::round(float(x)) к целому числу, будет выбрано число, не самое близкое к результату выражения?
У float → int тогда остается тогда только опасность выхода за границы представления int (std::numeric_limits<int>::min() ... std::numeric_limits<int>::max()).
Здравствуйте, T4r4sB, Вы писали:
M>>Отлично работает. А что тебя смущает?
TB>Что именно оно делает? Нужен ответ с отсылкой на доку.
Без ссылок не пойдет? Приведение типа double к int работает как отбрасывание дробной части. Это еще со времени чистой сишечки так работает, наверняка есть где-то в начале стандарта, там, где про фундаментальные типы пишут. 0.5 прибавляется, чтобы округление было по правилам — к ближайшему целому.
У Страуструпа в старом издании начала века в приложении B.6.2.6 о преобразовании плавающих в целые
Здравствуйте, B0FEE664, Вы писали:
BFE>Моя задача — округлить float до int. BFE>Вопрос: можно ли для этого использовать std::round? BFE>И если да, то как правильно избавится от предупреждения: warning C4244: '=': conversion from 'float' to 'int', possible loss of data.
Здравствуйте, Marty, Вы писали:
M>Без ссылок не пойдет? Приведение типа double к int работает как отбрасывание дробной части. Это еще со времени чистой сишечки так работает, наверняка есть где-то в начале стандарта, там, где про фундаментальные типы пишут. 0.5 прибавляется, чтобы округление было по правилам — к ближайшему целому.
M>У Страуструпа в старом издании начала века в приложении B.6.2.6 о преобразовании плавающих в целые
Ну если так, то что будет для отрицательных чисел?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, T4r4sB, Вы писали:
M>>У Страуструпа в старом издании начала века в приложении B.6.2.6 о преобразовании плавающих в целые
TB>Ну если так, то что будет для отрицательных чисел?
Конечно. Такие выражения игогда назвают "function-style conversions" и они эквивалентны "cast expressions": (int)std::round(3.14f). Вот что сказано по этому поводу в стандарте (C++17):
8.2.3 Explicit type conversion (functional notation)
2 If the initializer is a parenthesized single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (8.4)...
И вот еще цитата из 8.4:
8.4 Explicit type conversion (cast notation)
2 An explicit type conversion can be expressed using functional notation (8.2.3), a type conversion operator (dynamic_cast, static_cast, reinterpret_cast, const_cast), or the cast notation.
TB>>Ну если так, то что будет для отрицательных чисел?
M>Будет отброшена дробная часть, не?
Тут тебе придется с ним согласиться — твой вариант для отрицательных чисел будет работать не так как это ожидается от арифметического округления. Например, если мы захотм округлить -2.1, то получим: -2.1 -> -1.6 -> -1. Чтобы работало как надо, нужно слегка допилить:
R>Тут тебе придется с ним согласиться — твой вариант для отрицательных чисел будет работать не так как это ожидается от арифметического округления. Например, если мы захотм округлить -2.1, то получим: -2.1 -> -1.6 -> -1. Чтобы работало как надо, нужно слегка допилить:
R>
R>int i = (int)(d < 0 ? d - 0.5 : d + 0.5);
R>
Кто-то моё сообщение не дочитал
>для отрицательных надо отнять, если хочешь округление к большему по модулю
Здравствуйте, Marty, Вы писали:
M>Здравствуйте, T4r4sB, Вы писали:
M>>>У Страуструпа в старом издании начала века в приложении B.6.2.6 о преобразовании плавающих в целые
TB>>Ну если так, то что будет для отрицательных чисел?
M>Будет отброшена дробная часть, не?
Ты сначала прибавил 0.5, потом кастанул к инту, что произошло?
Подсказка: проделай это с числом -3.14
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, T4r4sB, Вы писали:
TB>>>Ну если так, то что будет для отрицательных чисел?
M>>Будет отброшена дробная часть, не?
TB>Ты сначала прибавил 0.5, потом кастанул к инту, что произошло? TB>Подсказка: проделай это с числом -3.14
Тоже не любишь до конца дочитывать?
>для отрицательных надо отнять, если хочешь округление к большему по модулю
Здравствуйте, T4r4sB, Вы писали:
>>>для отрицательных надо отнять, если хочешь округление к большему по модулю
TB>Круто, теперь у тебя есть функция с хитрым ветвлением внутри.
Здравствуйте, Marty, Вы писали:
M>Здравствуйте, T4r4sB, Вы писали:
>>>>для отрицательных надо отнять, если хочешь округление к большему по модулю
TB>>Круто, теперь у тебя есть функция с хитрым ветвлением внутри.
M>Чего тут хитрого?
Может, надёжнее сделать ветвление после округления?
f += 0.5f;
int i = int(f);
if (i>f) --i;
такой код правильно работает независимо от того, куда округляет каст
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
TB>такой код правильно работает независимо от того, куда округляет каст
Каст не округляет, а отбрасывает дробную часть. Если бы это было не так, или на разных платформах по разному, то это была бы глобальная жопа
ЗЫ Мой код (что-то типа inproc COM) работает под Win32 x86/64, Linux x86/64/Arm, и за него я вполне спокоен. Если что-то писать для embedded, то там могут быть нюансы, но там всё надо перепроверять в любом случае. Сейчас, кстати, под STM32 говнокодю
TB>>такой код правильно работает независимо от того, куда округляет каст
R>Ну где ж правильно-то? 2.9 должно округлиться до 3,
Да R>у тебя получается 2.
Неправда R>-2.1 должно округлиться до -2,
Да R>у тебя получается -3.
Неправда
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, T4r4sB, Вы писали:
R>>Ну где ж правильно-то? 2.9 должно округлиться до 3, TB>Да R>>у тебя получается 2. TB>Неправда R>>-2.1 должно округлиться до -2, TB>Да R>>у тебя получается -3. TB>Неправда
Да, все правильно, это я прогнал
Только чем это лучще? Вместо одного выражения: локальная переменная, условный оператор, модификация входного парамертра, дополнительное преобразование целого значения назад в число с плавающей точкой. В чем ты видишь выигрыш? Если это только ради того, чтоб подстраховаться на случай, если каст вдруг отработает не в ту сторону, то напрасно — как должен отработать каст прописано в стандарте языка.