round() для float в C++

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

double round(double);

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

можно найти ceil() и floor() в математике.н - но не round().

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

20 ответов


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

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

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


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

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

редактировать: начиная с C++11, есть std::round, std::lround и std::llround.


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

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

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

в C++11 это изменяется, так как C++11 полагается на проект стандарта C99 для C стандартная библиотека и, следовательно, обеспечивает std:: круглый и для интегральных возвращенных типов 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 ;

}

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

прокатка собственной версии раунда трудно

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

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

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

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

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

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

например:

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 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;
}

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


возможно, стоит отметить, что если вы хотите получить целочисленный результат от округления, вам не нужно передавать его через ceil или floor. И. Е.,

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

Он доступен с C++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

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

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


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

  1. округления преобразования
  2. преобразование типов.

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

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))

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


определенный тип округления также реализован в 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;
}

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


вы можете округлить до 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;
}

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

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

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

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

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

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


осторожно 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) дается здесь.


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

В C99

у нас есть следующий и и заголовок для макросов универсального типа.

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

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

gcc -lm -std=c99 ...

В C++11

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

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

здесь эквиваленты в пространстве имен std тоже.

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

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

С Порядковым Отдела

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

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

точность

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

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

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

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


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

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

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

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

предпочитаю rint() для повышения производительности: gcc и clang оба встроенных его легче, но gcc никогда не inlines nearbyint() (даже с -ffast-math)


gcc / clang для x86-64 и AArch64

я положил некоторые тестовые функции в Проводнике компилятора Мэтта Годболта, где вы можете увидеть source + ASM output (для нескольких компиляторов). Дополнительные сведения о чтении выходных данных компилятора см. В разделе это Q & A, и разговор Мэтта CppCon2017: " что мой компилятор сделал для меня в последнее время? Открепление крышки компилятора",

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

на x86, что проходной к SSE4.1 roundsd смогите автоматическ-векторизовать с SSE4.1 roundpd (или AVX vroundpd). (Преобразования FP - >integer также доступны в упакованной форме 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: inlines к одному insn по умолчанию.
  • std::rint:

    • x86 clang: строки для одного 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 default и большинства других.)
  • std::floor / std::ceil / std::trunc

    • x86 clang: строки для одного 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 или double -> long сначала, а затем усекает целое число до int. Это имеет значение для целых чисел вне диапазона: неопределенное поведение в C++, но хорошо определенное для инструкций x86 FP- > int (которые компилятор будет испускать, если он не видит UB во время компиляции при постоянном распространении, тогда разрешено делать код, который ломается, если он когда-либо выполнялся).

на x86 преобразование FP - >integer, которое переполняет целое число, производит INT_MIN или LLONG_MIN (немного-образец 0x8000000 или 64-разрядный эквивалент, только с набором знаков). Intel называет это значение" целочисленным неопределенным". (См.на cvttsd2si ручной ввод, инструкция SSE2, которая преобразует (с усечением) скалярный double в целое число со знаком. Он доступен с 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 inlines to roundsd / cvttsd2si. (пропустил оптимизации cvtsd2si). lrint не встроен в все.

  • x86 gcc6.x и ранее без -ffast-math: ни в коем случае inlines

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

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

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

функции 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
  • раунд -5.0 -5.0

Если вам нужно иметь возможность компилировать код в средах, поддерживающих стандарт C++11, но также необходимо иметь возможность компилировать тот же код в средах, которые его не поддерживают, вы можете использовать макрос функции для выбора между std::round() и пользовательской функцией для каждой системы. Просто пройдите -DCPP11 или /DCPP11 для компилятора, совместимого с C++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() в средах, которые не совместимы с C++11, включая сохранение бита знака для -0.0. Однако это может привести к небольшому снижению производительности и, вероятно, будет иметь проблемы с округлением некоторых известных "проблемных" значений с плавающей запятой, таких как 0.49999999999999994 или аналогичные значения.

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


основываясь на ответе 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);
    }

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

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

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

Я проверял my_roundf() исчерпывающе против newlib roundf() реализация с использованием компилятора Intel версии 13, с обоими /fp:strict и /fp:fast. Я также проверил, что версия файла соответствует 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;
}

Я использую следующую реализацию раунда в asm для архитектуры x86 и MS VS specific c++:

__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

// 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's 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. И это хорошо, потому что это относится к фактической поплавок. Не только визуально влияет на выход.


Я сделал так:

#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;
    }
}