문제

필요한 간단한 부동 소수점 라운딩 기능 따라서:

double round(double);

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

내가 찾을 수 있습니다 ceil()floor() 에서 수학이다.h-지 round().

그것은 존재하는 표준 C++라이브러리는 아래에서 또 다른 이름 또는것은 없습니까??

도움이 되었습니까?

해결책

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

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++03N1804 인)섹션 1.2 참조 규범:

라이브러리에 설명된 절 7 의 ISO/IEC9899:1990and 절 7 ISO/IEC9899/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부 trunc.

자신의 버전을 압연의 둥근드

압 자신은 아마도 가치가 있지 않으로 노력 보이는 것 보다 더:반올림하이 뜨고 가장 가까운 정수 1 부, 반올림하이 뜨고 가장 가까운 정수,제 2 부반올림하이 뜨고 가장 가까운 정수,3 부 설명:

예를 들면 일반적인 롤를 사용하여 구현하 std::floor 며 추가 0.5 이 작동하지 않는 모든 입력에 대한:

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

한 개의 입력이 실패한 0.49999999999999994, (보 live).

다른 일반적인 구현을 포함한 캐스팅 부동 소수점 입력을 필수적인 입력할 수 있는 호출을 정의되지 않은 행동을 한 경우에는 중요한 부분으로 표시할 수 없 대상에는 유형입니다.우리는 이것을 볼 수 있는 초안에서는 C++표준 단면도 4.9 부동 소수형 변환 는 말(중점을 나):

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

는 원인이 될 것이 오버플로우,(보 live).

우리가 볼 수 있는 방법을 어려운 이것이 정말로 보고 이에 응답 간단한 방법으로 구현하 라운드()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;
}

에 다른손이 없는 경우 다른 솔루션을 사용가능 newlib 할 수 있는 옵션이기 때문에 그것이 잘 테스트 구현합니다.

반올림에서 정수 결과를 원한다면 천장이나 바닥을 통과 할 필요가 없다는 점에 주목할 가치가 있습니다. 즉,

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에서 0, 가장 가까운 정수, 은행가의 반올림. 당신은 가장 가까운 정수를 요구하고 있습니다.

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

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

반올림 변환은 둥근 플로트/더블에 가장 가까운 바닥/천장 플로트/더블로 둥근 플로트를 의미합니다. 문제가 여기서 끝날 수 있습니다. 그러나 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;
}

이는 To-Integer 변환을 수행하는 경우에만 작동합니다.

다음과 같이 정밀도로 정밀도로 반올림 할 수 있습니다.

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 당신의 출력 round() an 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;
}

더 나은 성능의 이유 중 하나는 건너 뛰는 분기입니다.

이러한 일이 문제가되지 않을 사용하여 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 와 그 소리는 모두 인라인 더 쉽게 하지만 gcc 결코 inlines nearbyint() (도 -ffast-math)


gcc/그램를 위한 86-64AArch64

테스트 기능에 매트 Godbolt 의 컴파일러는 탐색기, 당신이 볼 수 있는 원본+asm 출력(에 대한 여러 컴파일러).더 읽기에 대한 컴파일러 출력을 참조하십시오 이 Q&A, 고,매트의 CppCon2017 이야기: "무엇이 내 컴파일러는 날 위해 최근?Unbolting 컴파일러의 뚜껑",

에 FP 코드,그것은 일반적으로 큰 승리를 인라인하는 작은 기능입니다.특히 비 Windows,표준화 컨벤션에는 어떤 통화 보존,레지스터 그렇게 컴파일러를 유지할 수 없는 어떤 FP XMM 레지스터에 값을 건너 call.그렇지 않은 경우에도 정말로 알 asm,당신은 여전히 쉽지 여부를 확인 그것은 단지 꼬리-부르는 라이브러리 함수 또는지 여부를 인라인 하나 또는 두 개의 수학이다.는 아무것도 inlines 하나 또는 두 개의 지침은 더 이상 기능 통화(이를 위해 특정 작업에 x86 또는 팔).

에 86 아무것도 inlines 을 SSE4.1 roundsd 할 수 있는 자동 벡터화와 SSE4.1 roundpd (또는 AVX vroundpd).(FP->정수의 변환에 사용할 수 있는 포장 SIMD 형태를 제외하고,FP->64-bit integer 필요 AVX512.)

  • std::nearbyint():

    • 86 그램:inlines 단일 insn 과 -msse4.1.
    • 86gcc:inlines 단일 insn 만 -msse4.1 -ffast-math, -해변에 있는 고급 레스토랑에서는 gcc5.4 이전.나중에 gcc 결코 inlines 그것은(아마도 그들은 깨닫지 않는 하나의 즉각적인 비트 억제할 수 있습니 정확하지 않은가요?그게 무슨 소용이지만,이전의 gcc 를 사용하여 동일한 즉각적으로 대 rint 는 인라인 it)
    • AArch64gcc6.3:inlines 단일 insn 기본적으로 합니다.
  • std::rint:

    • 86 그램:inlines 단일 insn 과 -msse4.1
    • 86gcc7:inlines 단일 insn 과 -msse4.1.(없이 SSE4.1,inlines 여러 지침)
    • 86gcc6.x 와 이전 버전에서 사용:inlines 단일 insn 과 -ffast-math -msse4.1.
    • AArch64gcc:inlines 단일 insn 기본적으로
  • std::round:

    • 86 그램:지 않는 인라인
    • 86gcc:inlines 여러 지침 -ffast-math -msse4.1, 을 필요로하는,두 개의 벡터 상수입니다.
    • AArch64gcc:inlines 단일 명령어(HW 에 대한 지원이 반올림 모드 뿐만 아니라 IEEE 기본이고 대부분 다른 사람.)
  • std::floor / std::ceil / std::trunc

    • 86 그램:inlines 단일 insn 과 -msse4.1
    • 86gcc7.x:inlines 단일 insn 과 -msse4.1
    • 86gcc6.x 와 이전 버전에서 사용:inlines 단일 insn 과 -ffast-math -msse4.1
    • AArch64gcc:inlines 기본적으로 단일 명령

반올림 int / long / long long:

당신은 여기에 두 가지 옵션:사 lrint (아 rint 하지만 반환합 long, 나 long longllrint다),또는 사용 FP->FP 라운딩 기능과 변환하는 정수형으로 정상적인 방법(으로 잘라내기).일부 컴파일러 최적화 방법 중 하나는 다른 것보다 낫다.

long l = lrint(x);

int  i = (int)rint(x);

Note int i = lrint(x) 변환 floatdouble -> long 먼저,다음을 자릅니다 정수 int.이 차이를 만드는 위한 범위 밖의 정수:정의되지 않은 행동에서는 C++지만,잘 정의된 대 86FP->int 지침에는(컴파일러가는 것을 방출하지 않으면 그것을 보고 UB 에서 컴파일 시간을 하고 있는 동안 일정한 전파,다음 그것을 만들 수 있는 코드는 휴식하면 그것은 이제까지 실행).

On x86,FP->integer 변환 넘치는 정수 생산 INT_MINLLONG_MIN (비트 패턴의 0x8000000 또는 64 비트 이와 동등한으로 로그인 비트로 설정).인텔 호출이"정수 무기한"값입니다.(참조하십시오 cvttsd2si 수동 입력,SSE2 교육 변환(으로 잘라내기)스칼라 두려인 정수입니다.그것의 사용으로 32-bit 또는 64-bit integer 대상(에서 64 비트 모드만 해당).도 cvtsd2si (환으로 현재의 반올림),무엇을 우리는 같은 컴파일러를 방출하는,그러나 불행하게도 gcc 와 그 소리 하지 않을 것이 없 -ffast-math.

또한 주는 FP 서 unsigned int/길이 적은 효율적인 x86(없이 AVX512).변환하는 32-bit unsigned 에서 64 비트 컴퓨터는 매우 저렴;그냥 변환하는 64 비트와 자릅니다.하지만 그렇지 않으면 그것은 상당히 느립니다.

  • 86 그램과 함께하지 않고/ -ffast-math -msse4.1: (int/long)rint inlines 하기 roundsd / cvttsd2si.(놓 최적화 cvtsd2si). lrint 지 않는 인라인에서 모두.

  • 86gcc6.x 와 이전 버전에서 사용할 수 없 -ffast-math:어느 방법으로 inlines

  • 86gcc7 없이 -ffast-math: (int/long)rint 라운드 변환 별도로(총 2 개의 지침의 SSE4.1 사용,그렇지 않으면의 무리와 함께 코드에 대한 인라인 rintroundsd). lrint 지 않는 인라인 요소입니다.
  • 86gcc -ffast-math: 모든 방법으로 인라인 cvtsd2si (최선), 요 SSE4.1.

  • AArch64gcc6.3 없이 -ffast-math: (int/long)rint inlines2 다. lrint 지 않는 인라인

  • AArch64gcc6.3 -ffast-math: (int/long)rint 컴파일하는 전화 lrint. lrint 지 않는 인라인 요소입니다.이 될 수 있을 놓쳤 최적화면 두 지침 우리는 얻을 수 없 -ffast-math 매우 느립니다.

조심하십시오 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에서

우리는 다음과 헤더가 있습니다u003Ctgmath.h> 유형의 매크로 용.

#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에는 다음과 추가 오버로드가 있습니다u003Ccmath> 그것은 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 표현의 Mantissa에서 중요한 숫자의 수가 아니라 인간으로서 우리의 소수 사고와 관련이 있습니다.

10은 5와 2의 산물이며 5와 2는 비교적 주요한 것입니다. 따라서 IEEE 부동 소수점 표준은 모든 이진 디지털 표현의 소수점으로 완벽하게 표현할 수 없습니다.

이것은 반올림 알고리즘의 문제가 아닙니다. 유형의 선택과 계산 설계, 데이터 입력 및 숫자 표시 중에 고려해야하는 것은 수학적 현실입니다. 애플리케이션에 이러한 소수된 이진 변환 문제를 보여주는 숫자를 표시하는 경우 응용 프로그램은 디지털 현실에 존재하지 않고 변경되어야하는 정확도를 시각적으로 표현합니다.

기능 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 .

이것은 -0.0에 대한 부호 비트의 보존을 포함하여 C ++ 11- 호환되지 않은 환경에서 std :: round ()에 근사합니다. 그러나 약간의 성능이 발생할 수 있으며 0.49999999999999994 또는 유사한 값과 같은 특정 "문제"부동 소수점 값을 반올림하는 데 문제가있을 수 있습니다.

또는 C ++ 11 호환 컴파일러에 액세스 할 수있는 경우 STD :: Round ()에서 그대로 잡을 수 있습니다. <cmath> 헤더를 사용하여 기능을 정의하지 않은 경우 기능을 정의하는 자신의 헤더를 만듭니다. 그러나 이것은 최적의 솔루션이 아닐 수도 있지만, 특히 여러 플랫폼에 대해 컴파일이 필요한 경우에 유의하십시오.

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

로 지적에서의 의견 및 다른 답변,the ISO C++표준 라이브러리를 추가하지 않았 round() 까지 ISO C++11,이 기능을 뽑아에서 참조하여 ISO C99 표준 수학 라이브러리입니다.

에 대한 긍정적인 피연산자에서[½, ub] round(x) == floor (x + 0.5), 디 ub 223float 매핑되는 경우 IEEE-754(2008) binary32, 252double 할 때 그것은 매핑을 IEEE-754(2008) binary64.숫자 23 52 에 해당하는의 수 저장 가수 비트에서 이러한 두 가지 부동 소수점 포맷입니다.에 대한 긍정적인 피연산자에서[+0,½) round(x) == 0, 고에 대한 긍정적인 피연산자(ub, +∞] round(x) == x.으로 기능에 대한 대칭 x 축 부정적인 인수 x 에 따라 취급될 수 있습니다 round(-x) == -round(x).

이 컴팩트 코드는 아래.컴파일에 적절한 수의계 지침에 걸쳐 다양한 플랫폼입니다.저는 관찰된 가장 컴팩트한 코드에 Gpu,가 my_roundf() 필요한 다스에 대한 지침.에 따라 프로세서 아키텍처 및 툴체인이 부동 소수점 기반 접근 방법이 될 수 있습니다 더 빠르게 또는 더 느린 보다 정수 기반 구현서 newlib 에서 참조 다른 대답이.

테스트 my_roundf() 철저하게 대 newlib roundf() 구현을 사용하여 인텔 컴파일러 버전 13,모두 /fp:strict/fp:fast.또한 것을 확인과 일치하 newlib roundf()mathimf 의 라이브러리 인텔 컴파일러입니다.철저한 테스트가 불가능한 이중 정밀도 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;
}

X86 아키텍처 및 MS VS 특정 C ++에 대한 ASM의 라운드 구현을 사용합니다.

__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

"N"소수점으로 떠 다니는 값을 반올림하는 가장 좋은 방법은 O (1) 시간과 마찬가지로 다음과 같습니다.

우리는 값을 3 개 장소 (즉, n = 3) 씩 반올림해야합니다.

float a=47.8732355;
printf("%.3f",a);
// 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;
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top