Подтвердить что ты не робот

Round() для float в С++

Мне нужна простая функция округления с плавающей запятой, таким образом:

double round(double);

round(0.1) = 0
round(-0.1) = 0
round(-0.9) = -1

Я могу найти ceil() и floor() в math.h, но не round().

Он присутствует в стандартной библиотеке С++ под другим именем или отсутствует?

4b9b3361

Ответ 1

Там нет round() в стандартной библиотеке С++ 98. Вы можете написать один самостоятельно, хотя. Ниже приведена реализация круглого с половиной:

double round(double d)
{
  return floor(d + 0.5);
}

Вероятная причина того, что в стандартной библиотеке С++ 98 нет функции округления, заключается в том, что она может быть реализована различными способами. Выше приведен один из распространенных способов, но есть и другие, такие как округление до четности, которое является менее предвзятым и, как правило, лучше, если вы собираетесь делать много округлений; это немного сложнее реализовать, хотя.

Ответ 2

Boost предлагает простой набор функций округления.

#include <boost/math/special_functions/round.hpp>

double a = boost::math::round(1.5); // Yields 2.0
int b = boost::math::iround(1.5); // Yields 2 as an integer

Для получения дополнительной информации см. документацию Boost.

Изменить. С С++ 11 есть std::round, std::lround и std::llround.

Ответ 3

Стандарт С++ 03 полагается на стандарт C90, для которого стандарт вызывает стандартную библиотеку C, которая включена в черновик стандарта С++ 03 (ближайший общедоступный проект стандарта до С++ 03 - N1804) section 1.2 Нормативные ссылки:

Библиотека, описанная в разделе 7 ИСО/МЭК 9899: 1990 и 7 ISO/IEC 9899/Amd.1:1995 далее именуется Стандартом C Библиотека. 1)

Если мы перейдем к документации C для round, lround, llround на cppreference, мы увидим, что круглые и связанные функции являются частью C99 и таким образом, не будет доступна в С++ 03 или ранее.

В С++ 11 это изменяется, поскольку С++ 11 полагается на стандартный проект C99 для стандартной библиотеки C и поэтому предоставляет std:: round и для интеграла возвращаемые типы std:: lround, std:: llround:

#include <iostream>
#include <cmath>

int main()
{
    std::cout << std::round( 0.4 ) << " " << std::lround( 0.4 ) << " " << std::llround( 0.4 ) << std::endl ;
    std::cout << std::round( 0.5 ) << " " << std::lround( 0.5 ) << " " << std::llround( 0.5 ) << std::endl ;
    std::cout << std::round( 0.6 ) << " " << std::lround( 0.6 ) << " " << std::llround( 0.6 ) << std::endl ;
}

Другой вариант также с C99 будет std:: trunc, который:

Вычисляет ближайшее целое число, не большее по величине, чем arg.

#include <iostream>
#include <cmath>

int main()
{
    std::cout << std::trunc( 0.4 ) << std::endl ;
    std::cout << std::trunc( 0.9 ) << std::endl ;
    std::cout << std::trunc( 1.1 ) << std::endl ;

}

Если вам нужно поддерживать приложения, отличные от С++ 11, лучше всего использовать boost round, iround, lround, llround или boost trunc.

Роллинг вашей версии раунда тяжелый

Роллинг, вероятно, не стоит усилий, поскольку Сложнее, чем выглядит: округление float до ближайшего целого, часть 1, округление float до ближайшего целого, часть 2 и округление float до ближайшего целого, часть 3 объясните:

Например, общий запуск вашей реализации с использованием std::floor и добавление 0.5 не работает для всех входов:

double myround(double d)
{
  return std::floor(d + 0.5);
}

Один вход для этого не будет: 0.49999999999999994, (посмотреть его в прямом эфире).

Другая распространенная реализация включает в себя выбор типа с плавающей точкой на интегральный тип, который может вызывать поведение undefined в случае, когда неотъемлемая часть не может быть представлена ​​в типе адресата. Мы можем видеть это из проекта стандартного раздела С++ 4.9 Преобразования с плавающим интегралом, в котором говорится (выделено мной):

Значение типа с плавающей запятой может быть преобразовано в prvalue целочисленный тип. Преобразование усекает; то есть дробная часть отбрасывается. Поведение undefined, если усеченное значение не может будет отображаться в типе назначения. [...]

Например:

float myround(float f)
{
  return static_cast<float>( static_cast<unsigned int>( f ) ) ;
}

Учитывая std::numeric_limits<unsigned int>::max() is 4294967295, то следующий вызов:

myround( 4294967296.5f ) 

приведет к переполнению, (увидеть его в прямом эфире).

Мы можем видеть, насколько это сложно, глядя на этот ответ на Краткий способ реализации round() в C?, который ссылается на версию newlibs одинарной точности float round, Это очень длинная функция для чего-то, что кажется простым. Кажется маловероятным, чтобы любой, кто не знал о реализации с плавающей точкой, мог правильно реализовать эту функцию:

float roundf(x)
{
  int signbit;
  __uint32_t w;
  /* Most significant word, least significant word. */
  int exponent_less_127;

  GET_FLOAT_WORD(w, x);

  /* Extract sign bit. */
  signbit = w & 0x80000000;

  /* Extract exponent field. */
  exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;

  if (exponent_less_127 < 23)
    {
      if (exponent_less_127 < 0)
        {
          w &= 0x80000000;
          if (exponent_less_127 == -1)
            /* Result is +1.0 or -1.0. */
            w |= ((__uint32_t)127 << 23);
        }
      else
        {
          unsigned int exponent_mask = 0x007fffff >> exponent_less_127;
          if ((w & exponent_mask) == 0)
            /* x has an integral value. */
            return x;

          w += 0x00400000 >> exponent_less_127;
          w &= ~exponent_mask;
        }
    }
  else
    {
      if (exponent_less_127 == 128)
        /* x is NaN or infinite. */
        return x + x;
      else
        return x;
    }
  SET_FLOAT_WORD(x, w);
  return x;
}

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

Ответ 4

Возможно, стоит отметить, что если вы хотите получить целочисленный результат из округления, вам не нужно пропускать его ни через пол, ни на пол. То есть.

int round_int( double r ) {
    return (r > 0.0) ? (r + 0.5) : (r - 0.5); 
}

Ответ 5

Он доступен с С++ 11 в cmath (в соответствии с http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf)

#include <cmath>
#include <iostream>

int main(int argc, char** argv) {
  std::cout << "round(0.5):\t" << round(0.5) << std::endl;
  std::cout << "round(-0.5):\t" << round(-0.5) << std::endl;
  std::cout << "round(1.4):\t" << round(1.4) << std::endl;
  std::cout << "round(-1.4):\t" << round(-1.4) << std::endl;
  std::cout << "round(1.6):\t" << round(1.6) << std::endl;
  std::cout << "round(-1.6):\t" << round(-1.6) << std::endl;
  return 0;
}

Вывод:

round(0.5):  1
round(-0.5): -1
round(1.4):  1
round(-1.4): -1
round(1.6):  2
round(-1.6): -2

Ответ 6

Обычно он реализуется как floor(value + 0.5).

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

Ответ 7

Есть 2 проблемы, на которые мы смотрим:

  • округление конверсий
  • преобразование типа.

Конверсии округления означают округление ± поплавок/с двойным на ближайший пол/полый плавающий/двойной. Может быть, ваша проблема заканчивается здесь. Но если вы ожидаете вернуть Int/Long, вам нужно выполнить преобразование типов, и, таким образом, проблема с "переполнением" может повлиять на ваше решение. SO, выполните проверку на ошибку в вашей функции.

long round(double x) {
   assert(x >= LONG_MIN-0.5);
   assert(x <= LONG_MAX+0.5);
   if (x >= 0)
      return (long) (x+0.5);
   return (long) (x-0.5);
}

#define round(x) ((x) < LONG_MIN-0.5 || (x) > LONG_MAX+0.5 ?\
      error() : ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))

from: http://www.cs.tut.fi/~jkorpela/round.html

Ответ 8

В Boost также реализуется определенный тип округления:

#include <iostream>

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

template<typename T, typename S> T round2(const S& x) {
  typedef boost::numeric::conversion_traits<T, S> Traits;
  typedef boost::numeric::def_overflow_handler OverflowHandler;
  typedef boost::numeric::RoundEven<typename Traits::source_type> Rounder;
  typedef boost::numeric::converter<T, S, Traits, OverflowHandler, Rounder> Converter;
  return Converter::convert(x);
}

int main() {
  std::cout << round2<int, double>(0.1) << ' ' << round2<int, double>(-0.1) << ' ' << round2<int, double>(-0.9) << std::endl;
}

Обратите внимание, что это работает, только если вы выполняете преобразование целых чисел.

Ответ 9

Вы можете округлить до n цифр с помощью:

double round( double x )
{
const double sd = 1000; //for accuracy to 3 decimal places
return int(x*sd + (x<0? -0.5 : 0.5))/sd;
}

Ответ 10

Если вы в конечном итоге хотите преобразовать вывод double вашей функции round() в int, тогда принятые решения этого вопроса будут выглядеть примерно так:

int roundint(double r) {
  return (int)((r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5));
}

Это время работает со скоростью 8.88 ns на моей машине при передаче в равномерно случайных значениях.

Ниже, насколько я могу судить, функционально эквивалентно, но часы на 2.48 ns на моей машине, для значительного преимущества производительности:

int roundint (double r) {
  int tmp = static_cast<int> (r);
  tmp += (r-tmp>=.5) - (r-tmp<=-.5);
  return tmp;
}

Среди причин лучшей производительности - пропущенное ветвление.

Ответ 11

Остерегайтесь floor(x+0.5). Вот что может случиться для нечетных чисел в диапазоне [2 ^ 52,2 ^ 53]:

-bash-3.2$ cat >test-round.c <<END

#include <math.h>
#include <stdio.h>

int main() {
    double x=5000000000000001.0;
    double y=round(x);
    double z=floor(x+0.5);
    printf("      x     =%f\n",x);
    printf("round(x)    =%f\n",y);
    printf("floor(x+0.5)=%f\n",z);
    return 0;
}
END

-bash-3.2$ gcc test-round.c
-bash-3.2$ ./a.out
      x     =5000000000000001.000000
round(x)    =5000000000000001.000000
floor(x+0.5)=5000000000000002.000000

Это http://bugs.squeak.org/view.php?id=7134. Используйте решение, подобное одному из @konik.

Моя собственная надежная версия будет примерно такой:

double round(double x)
{
    double truncated,roundedFraction;
    double fraction = modf(x, &truncated);
    modf(2.0*fraction, &roundedFraction);
    return truncated + roundedFraction;
}

Еще одна причина, чтобы избежать пола (x + 0,5), дана здесь.

Ответ 12

В наши дни не должно быть проблем с использованием компилятора С++ 11, который включает в себя математическую библиотеку C99/С++ 11. Но тогда возникает вопрос: какую функцию округления вы выбираете?

C99/С++ 11 round() часто не является фактически функцией округления, которую вы хотите. Он использует фанки округлый режим, который округляется от 0 в качестве тай-брейка на полпути случаях (+-xxx.5000). Если вам действительно нужен этот режим округления или вы нацеливаетесь на реализацию С++, где round() быстрее, чем rint(), используйте его (или подражайте его поведению с одним из других ответов на этот вопрос, который взял его на себя значение и тщательно воспроизвести это специфическое поведение округления.)

round() округление отличается от стандартного IEEE754 раунда до ближайшего режима даже с тай-брейком. Nearest-even избегает статистического смещения в средней величине чисел, но делает смещение к четным числам.

Есть две функции округления математической библиотеки, которые используют текущий режим округления по умолчанию: std::nearbyint() и std::rint(), оба добавлены в C99/С++ 11, поэтому они доступны в любое время std::round(). Единственное отличие состоит в том, что nearbyint никогда не вызывает FE_INEXACT.

Предпочитает rint() по соображениям производительности: gcc и clang как inline его легче, но gcc никогда не строит nearbyint() (даже с -ffast-math)


gcc/clang для x86-64 и AArch64

Я помещал некоторые тестовые функции в Matt Architects Compiler Explorer, где вы можете видеть исходный + asm-выход ( для нескольких компиляторов). Подробнее о чтении вывода компилятора см. В этом Q & A, а Matt CppCon2017: Что Скомпилировал ли мой компилятор для меня в последнее время? Откручивание крышки компилятора,

В коде FP обычно это большая победа для создания небольших функций. Особенно на не-Windows, где стандартное соглашение о вызове не имеет регистров, сохраняющих вызовы, поэтому компилятор не может сохранять значения FP в регистрах XMM через call. Таким образом, даже если вы действительно не знаете asm, вы все равно можете легко увидеть, является ли это просто вызовом хвоста библиотечной функции или встроен ли он в одну или две математические инструкции. Все, что связано с одной или двумя инструкциями, лучше, чем вызов функции (для этой конкретной задачи на x86 или ARM).

На x86 все, что встроено в SSE4.1 roundsd, может автоматически сгенерировать с помощью SSE4.1 roundpd (или AVX vroundpd). (FP- > целые преобразования также доступны в упакованной SIMD-форме, за исключением FP- > 64-битного целого числа, которое требует AVX512.)

  • std::nearbyint():

    • x86 clang: встроен в один insn с -msse4.1.
    • x86 gcc: встроен в один insn только с -msse4.1 -ffast-math и только на gcc 5.4 и ранее. Позже gcc никогда не строит его (возможно, они не понимали, что один из непосредственных битов может подавить неточное исключение? То, что использует clang, но более старый gcc использует то же самое, что и для rint, когда он делает это в строке)
    • AArch64 gcc6.3: по умолчанию используется один insn.
  • std::rint:

    • x86 clang: inlines к одному insn с -msse4.1
    • x86 gcc7: строка с одним insn с -msse4.1. (Без SSE4.1, в строках нескольких инструкций)
    • x86 gcc6.x и более ранние версии: встроенные в один insn с -ffast-math -msse4.1.
    • AArch64 gcc: по умолчанию используется один insn
  • std::round:

    • x86 clang: не встроен
    • x86 gcc: строка для нескольких инструкций с -ffast-math -msse4.1, требующая двух векторных констант.
    • AArch64 gcc: встроенные в одну команду (поддержка HW для этого режима округления, а также по умолчанию IEEE и большинство других.)
  • std::floor/std::ceil/std::trunc

    • x86 clang: inlines к одному insn с -msse4.1
    • x86 gcc7.x: встроенные в один insn с -msse4.1
    • x86 gcc6.x и более ранние версии: встроенные в один insn с -ffast-math -msse4.1
    • AArch64 gcc: по умолчанию по умолчанию одна команда

Округление до int/long/long long:

Здесь у вас есть два варианта: используйте lrint (например, rint, но верните long или long long для llrint)) или используйте функцию округления FP- > FP, а затем преобразуйте в целочисленный тип обычным способом (с усечением). Некоторые компиляторы оптимизируют один путь лучше, чем другие.

long l = lrint(x);

int  i = (int)rint(x);

Обратите внимание, что int i = lrint(x) сначала преобразует float или doublelong, а затем обрезает целое число до int. Это имеет значение для целых чисел вне диапазона: Undefined Поведение на С++, но хорошо определенное для инструкций x86 FP → int (которые компилятор будет испускать, если не видит UB во время компиляции при постоянном распространении, то это позволило сделать код, который ломается, если он когда-либо выполняется).

На x86 преобразование целочисленного числа FP- > , которое переполняет целое число, создает INT_MIN или LLONG_MIN (бит-шаблон 0x8000000 или 64-битный эквивалент, только с установленным битом). Intel называет это "целочисленным неопределенным" значением. (См. руководство пользователя cvttsd2si, инструкция SSE2, которая преобразует (с усечением) скалярное двойное целое число со знаком. Оно доступно с 32-битным или 64-разрядный целочисленный адрес (только в 64-битном режиме). Также есть cvtsd2si (конвертировать с текущим режимом округления), что мы хотели бы, чтобы компилятор испустил, но, к сожалению, gcc и clang не будут делать что без -ffast-math.

Также будьте осторожны, что FP to/from unsigned int/long менее эффективен на x86 (без AVX512). Преобразование в 32-разрядное беззнаковое на 64-битной машине довольно дешево; просто конвертируйте в 64-битную подпись и усечь. Но в противном случае это значительно медленнее.

  • x86 clang с/без -ffast-math -msse4.1: (int/long)rint в строке roundsd/cvttsd2si. (пропущенная оптимизация до cvtsd2si). lrint не является встроенным.

  • x86 gcc6.x и ранее без -ffast-math: ни один из способов вставки

  • x86 gcc7 без -ffast-math: (int/long)rint раунды и конверты отдельно (с двумя полными инструкциями SSE4.1 включено, в противном случае с кучей кода, заключенным в rint без roundsd). lrint не встроен.
  • x86 gcc с -ffast-math: все пути inline до cvtsd2si (оптимальные), нет необходимости в SSE4.1.

  • AArch64 gcc6.3 без -ffast-math: (int/long)rint в строках до 2 инструкций. lrint не встроен

  • AArch64 gcc6.3 с -ffast-math: (int/long)rint компилируется для вызова lrint. lrint не является встроенным. Это может быть пропущенная оптимизация, если две команды, которые мы получаем без -ffast-math, очень медленные.

Ответ 13

Функция double round(double) с использованием функции modf:

double round(double x)
{
    using namespace std;

    if ((numeric_limits<double>::max() - 0.5) <= x)
        return numeric_limits<double>::max();

    if ((-1*std::numeric_limits<double>::max() + 0.5) > x)
        return (-1*std::numeric_limits<double>::max());

    double intpart;
    double fractpart = modf(x, &intpart);

    if (fractpart >= 0.5)
        return (intpart + 1);
    else if (fractpart >= -0.5)
        return intpart;
    else
        return (intpart - 1);
    }

Чтобы быть компилируемыми, необходимо включить "math.h" и "лимиты". Функция работает в соответствии со следующей схемой округления:

  • раунд 5.0 5.0
  • раунд 3.8 4.0
  • раунд 2.3 - 2.0
  • раунд 1.5 - 2.0
  • раунд 0.501 - 1.0
  • раунд 0.5 = 1.0
  • раунд 0.499 = 0.0
  • раунд 0,01 равен 0.0
  • раунд 0.0 0.0
  • раунд -0.01 - -0.0
  • раунд -0.499 - -0.0
  • раунд -0.5 - -0.0
  • раунд -0.501 равен -1.0
  • раунд -1.5 равен -1.0
  • раунд -2.3 -2.0
  • раунд -3.8 составляет -4.0
  • round -5.0 - -5.0

Ответ 14

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

В C99

У нас есть следующие и и header < tgmath.h > для макросов типового типа.

#include <math.h>
double round (double x);
float roundf (float x);
long double roundl (long double x);

Если вы не можете скомпилировать это, вы, вероятно, не учли математическую библиотеку. Команда, подобная этой, работает на каждом компиляторе C, который у меня есть (несколько).

gcc -lm -std=c99 ...

В С++ 11

У нас есть следующие и дополнительные перегрузки в #include <cmath> которые полагаются на плавающую точку с двойной точностью IEEE.

#include <math.h>
double round (double x);
float round (float x);
long double round (long double x);
double round (T x);

Существуют эквиваленты в пространстве имен std.

Если вы не можете скомпилировать это, вы можете использовать компиляцию C вместо С++. Следующая базовая команда не создает ни ошибок, ни предупреждений с g++ 6.3.1, x86_64-w64-mingw32-g++ 6.3.0, clang-x86_64 ++ 3.8.0 и Visual С++ 2015 Community.

g++ -std=c++11 -Wall

С ординальной дивизией

При делении двух порядковых чисел, где T является коротким, int, long или другим порядковым номером, это выражение округления.

T roundedQuotient = (2 * integerNumerator + 1)
    / (2 * integerDenominator);

Точность

Нет сомнений в том, что в операциях с плавающей запятой появляются нечетные неточности, но это происходит только тогда, когда цифры появляются и имеют мало общего с округлением.

Источником является не только количество значащих цифр в мантиссе представления IEEE числа с плавающей запятой, оно связано с нашим десятичным мышлением как людьми.

Ten - произведение пяти и двух, а 5 и 2 - относительно простые. Поэтому стандарты с плавающей точкой IEEE не могут быть представлены в качестве десятичных чисел для всех двоичных цифровых представлений.

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

Ответ 15

Основываясь на ответе Kalaxy, следующим является шаблонное решение, которое округляет любое число с плавающей точкой до ближайшего целочисленного типа на основе естественного округления. Он также выдает ошибку в режиме отладки, если значение вне диапазона целочисленного типа, тем самым служа в качестве жизнеспособной библиотечной функции.

    // round a floating point number to the nearest integer
    template <typename Arg>
    int Round(Arg arg)
    {
#ifndef NDEBUG
        // check that the argument can be rounded given the return type:
        if (
            (Arg)std::numeric_limits<int>::max() < arg + (Arg) 0.5) ||
            (Arg)std::numeric_limits<int>::lowest() > arg - (Arg) 0.5)
            )
        {
            throw std::overflow_error("out of bounds");
        }
#endif

        return (arg > (Arg) 0.0) ? (int)(r + (Arg) 0.5) : (int)(r - (Arg) 0.5);
    }

Ответ 16

Если вам нужно скомпилировать код в средах, поддерживающих стандарт С++ 11, но также необходимо иметь возможность компилировать тот же код в средах, которые его не поддерживают, вы можете использовать макрос функции для выберите между std:: round() и пользовательскую функцию для каждой системы. Просто передайте -DCPP11 или /DCPP11 компилятору, совместимому с С++ 11 (или используйте его встроенные макросы версии), и создайте заголовок следующим образом:

// File: rounding.h
#include <cmath>

#ifdef CPP11
    #define ROUND(x) std::round(x)
#else    /* CPP11 */
    inline double myRound(double x) {
        return (x >= 0.0 ? std::floor(x + 0.5) : std::ceil(x - 0.5));
    }

    #define ROUND(x) myRound(x)
#endif   /* CPP11 */

Для быстрого примера см. http://ideone.com/zal709.

Это приближается к std:: round() в средах, которые не совместимы с С++ 11, включая сохранение знакового бита для -0.0. Однако это может привести к небольшому результативному результату и, вероятно, будет иметь проблемы с округлением определенных известных "проблемных" значений с плавающей запятой, таких как 0,4999999999999999994 или аналогичных значений.

В качестве альтернативы, если у вас есть доступ к компилятору, совместимому с С++ 11, вы можете просто захватить std:: round() из заголовка <cmath> и использовать его для создания собственного заголовка, который определяет функцию, если она еще не определены. Обратите внимание, что это может быть не оптимальное решение, особенно если вам нужно скомпилировать несколько платформ.

Ответ 17

Как указано в комментариях и других ответах, стандартная библиотека ISO С++ не добавляла round() до ISO С++ 11, когда эта функция была вытащена ссылкой на стандартную математическую библиотеку ISO C99.

Для положительных операндов в [½, ub] round(x) == floor (x + 0.5), где ub - 2 23 для float при сопоставлении с IEEE-754 (2008) binary32 и 2 52 для double, когда он сопоставляется с IEEE-754 (2008) binary64. Номера 23 и 52 соответствуют количеству сохраненных битов мантиссы в этих двух форматах с плавающей запятой. Для положительных операндов в [+0, ½) round(x) == 0 и для положительных операндов в (ub, + ∞] round(x) == x. Поскольку функция симметрична относительно оси x, отрицательные аргументы x можно обрабатывать согласно round(-x) == -round(x).

Это приводит к компактному коду ниже. Он компилируется в разумное количество машинных инструкций на разных платформах. Я заметил самый компактный код на графических процессорах, где my_roundf() требуется около дюжины инструкций. В зависимости от архитектуры процессора и инструментальной привязки этот подход с плавающей запятой может быть как быстрее, так и медленнее, чем реализация на основе целого ряда из newlib, на которую ссылается другой ответ.

Я тестировал my_roundf() исчерпывающе по сравнению с реализацией newlib roundf() с использованием компилятора Intel версии 13, с /fp:strict и /fp:fast. Я также проверил, что версия newlib соответствует roundf() в библиотеке mathimf компилятора Intel. Исчерпывающее тестирование невозможно для двойной точности round(), однако код структурно идентичен реализации с одной точностью.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>

float my_roundf (float x)
{
    const float half = 0.5f;
    const float one = 2 * half;
    const float lbound = half;
    const float ubound = 1L << 23;
    float a, f, r, s, t;
    s = (x < 0) ? (-one) : one;
    a = x * s;
    t = (a < lbound) ? x : s;
    f = (a < lbound) ? 0 : floorf (a + half);
    r = (a > ubound) ? x : (t * f);
    return r;
}

double my_round (double x)
{
    const double half = 0.5;
    const double one = 2 * half;
    const double lbound = half;
    const double ubound = 1ULL << 52;
    double a, f, r, s, t;
    s = (x < 0) ? (-one) : one;
    a = x * s;
    t = (a < lbound) ? x : s;
    f = (a < lbound) ? 0 : floor (a + half);
    r = (a > ubound) ? x : (t * f);
    return r;
}

uint32_t float_as_uint (float a)
{
    uint32_t r;
    memcpy (&r, &a, sizeof(r));
    return r;
}

float uint_as_float (uint32_t a)
{
    float r;
    memcpy (&r, &a, sizeof(r));
    return r;
}

float newlib_roundf (float x)
{
    uint32_t w;
    int exponent_less_127;

    w = float_as_uint(x);
    /* Extract exponent field. */
    exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;
    if (exponent_less_127 < 23) {
        if (exponent_less_127 < 0) {
            /* Extract sign bit. */
            w &= 0x80000000;
            if (exponent_less_127 == -1) {
                /* Result is +1.0 or -1.0. */
                w |= ((uint32_t)127 << 23);
            }
        } else {
            uint32_t exponent_mask = 0x007fffff >> exponent_less_127;
            if ((w & exponent_mask) == 0) {
                /* x has an integral value. */
                return x;
            }
            w += 0x00400000 >> exponent_less_127;
            w &= ~exponent_mask;
        }
    } else {
        if (exponent_less_127 == 128) {
            /* x is NaN or infinite so raise FE_INVALID by adding */
            return x + x;
        } else {
            return x;
        }
    }
    x = uint_as_float (w);
    return x;
}

int main (void)
{
    uint32_t argi, resi, refi;
    float arg, res, ref;

    argi = 0;
    do {
        arg = uint_as_float (argi);
        ref = newlib_roundf (arg);
        res = my_roundf (arg);
        resi = float_as_uint (res);
        refi = float_as_uint (ref);
        if (resi != refi) { // check for identical bit pattern
            printf ("!!!! arg=%08x  res=%08x  ref=%08x\n", argi, resi, refi);
            return EXIT_FAILURE;
        }
        argi++;
    } while (argi);
    return EXIT_SUCCESS;
}

Ответ 18

Я использую следующую реализацию round в asm для архитектуры x86 и MS VS специфического С++:

__forceinline int Round(const double v)
{
    int r;
    __asm
    {
        FLD     v
        FISTP   r
        FWAIT
    };
    return r;
}

UPD: возврат двойного значения

__forceinline double dround(const double v)
{
    double r;
    __asm
    {
        FLD     v
        FRNDINT
        FSTP    r
        FWAIT
    };
    return r;
}

Вывод:

dround(0.1): 0.000000000000000
dround(-0.1): -0.000000000000000
dround(0.9): 1.000000000000000
dround(-0.9): -1.000000000000000
dround(1.1): 1.000000000000000
dround(-1.1): -1.000000000000000
dround(0.49999999999999994): 0.000000000000000
dround(-0.49999999999999994): -0.000000000000000
dround(0.5): 0.000000000000000
dround(-0.5): -0.000000000000000

Ответ 19

Лучший способ округлить плавающее значение с помощью "n" десятичных разрядов, как показано в O (1) раз: -

Мы должны округлить значение на 3 места, то есть n = 3. Так что

float a=47.8732355;
printf("%.3f",a);

Ответ 20

// Convert the float to a string
// We might use stringstream, but it looks like it truncates the float to only
//5 decimal points (maybe that what you want anyway =P)

float MyFloat = 5.11133333311111333;
float NewConvertedFloat = 0.0;
string FirstString = " ";
string SecondString = " ";
stringstream ss (stringstream::in | stringstream::out);
ss << MyFloat;
FirstString = ss.str();

// Take out how ever many decimal places you want
// (this is a string it includes the point)
SecondString = FirstString.substr(0,5);
//whatever precision decimal place you want

// Convert it back to a float
stringstream(SecondString) >> NewConvertedFloat;
cout << NewConvertedFloat;
system("pause");

Это может быть неэффективный грязный способ конверсии, но, черт возьми, он работает lol. И это хорошо, потому что это относится к фактическому поплавку. Не просто визуально визуально отображаться.

Ответ 21

Принятый ответ неверен.

Например, round (-3.7) = -4, но он даст вам -3 вместо -4.

Сделайте это (предположим, что x двойное):

#include "math.h"


double n = floor( x + 0.5  );

или

int n = (int)floor( x + 0.5  );

если вы хотите сделать бросок, так как вы округлите число.

Ответ 22

Я сделал это:

#include <cmath.h>

using namespace std;

double roundh(double number, int place){

    /* place = decimal point. Putting in 0 will make it round to whole
                              number. putting in 1 will round to the
                              tenths digit.
    */

    number *= 10^place;
    int istack = (int)floor(number);
    int out = number-istack;
    if (out < 0.5){
        floor(number);
        number /= 10^place;
        return number;
    }
    if (out > 0.4) {
        ceil(number);
        number /= 10^place;
        return number;
    }
}