[c++] C ++에서 float의 round ()

간단한 부동 소수점 반올림 함수가 필요합니다.

double round(double);

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

내가 찾을 수 ceil()floor()math.h에 -하지만round() .

표준 C ++ 라이브러리에 다른 이름으로 존재합니까, 아니면 없습니까?



답변

C ++ 98 표준 라이브러리에는 round ()가 없습니다. 그래도 직접 쓸 수 있습니다. 다음은 반감기 구현입니다 .

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

C ++ 98 표준 라이브러리에 라운드 함수가없는 가능한 이유는 실제로 다른 방식으로 구현 될 수 있기 때문입니다. 위의 한 가지 일반적인 방법이지만 round-to-even 과 같은 다른 방법이 있습니다. 편향이 적고 많은 라운딩을 할 경우 일반적으로 더 좋습니다. 그래도 구현하기가 조금 더 복잡합니다.


답변

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 문서를 참조하십시오 .

편집 : C ++ 11가 있기 때문에 std::round, std::lround하고std::llround .


답변

는 C ++ 03 표준에 대한 C90 표준에 의존하는 것을 표준 통화량 표준 C 라이브러리 초안 C ++ 03 표준에 덮여 ( C ++ 03 N1804에 가장 가까운 공개 초안 표준 섹션) 1.2 참조 규격 :

ISO / IEC 9899 : 1990의 7 절과 ISO / IEC 9899 / Amd.1 : 1995의 7 절에 설명 된 라이브러리를 이하 표준 C 라이브러리라고합니다. 1)

우리가에 가면 라운드, lround에 대한 C 문서, cppreference에 llround은 우리가 볼 수있는 및 관련 기능의 일부 C99 때문에 03 또는 이전 ++ C에서 사용할 수 없습니다.

C ++ 11에서는 C ++ 11이 C 표준 라이브러리 에 대한 C99 초안 표준을 사용 하므로 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 ;

}

비 C ++ 11 응용 프로그램을 지원 해야하는 경우 가장 좋은 방법은 boost round, 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()4294967295다음 호출은 :

myround( 4294967296.5f ) 

오버플로가 발생합니다 ( 살아보십시오 ).

C에서 round ()를 구현 하는 간결한 방법에 대한이 답변을 보면 이것이 실제로 얼마나 어려운지 알 수 있습니까? 단정도 플로트 라운드의 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;
}

반면에 다른 솔루션을 사용할 수없는 경우 newlib 은 테스트가 잘된 구현이므로 옵션이 될 수 있습니다.


답변

반올림에서 정수 결과를 원한다면 ceil 또는 floor를 통해 전달할 필요가 없습니다. 즉,

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


답변

cmath에서 C ++ 11부터 사용할 수 있습니다 ( 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) .

편집 : 내가 알고있는 적어도 3 개의 반올림 알고리즘이 있기 때문에 아마도 라운드라고 부를 수 없습니다 : 0으로 반올림, 가장 가까운 정수로 반올림 및 은행가 반올림 가장 가까운 정수에 반올림을 요청합니다.


답변

우리 가보고있는 두 가지 문제가 있습니다 :

  1. 반올림 변환
  2. 유형 변환.

반올림 변환은 반올림 ± float / double을 가장 가까운 floor / 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