C에서 소수점 뒤에 두 자리로만 float 값을 제한하려면 어떻게합니까?


215

C에서 부동 소수점 값 (예 : 37.777779)을 소수점 이하 두 자리 (37.78)로 반올림하려면 어떻게해야합니까?


15
float(및 double)은 십진 부동 소수점이 아니기 때문에 이진 부동 소수점 이기 때문에 숫자 자체를 올바르게 반올림 할 수 없으므로 소수 자릿수로 반올림하는 것은 의미가 없습니다. 그러나 출력을 반올림 할 수 있습니다.
Pavel Minaev

63
의미가 없습니다. 정확하지 않습니다. 큰 차이가 있습니다.
Brooks Moses

2
어떤 반올림을 기대하십니까? 반올림 또는 반올림?
Truthseeker Rangwan

답변:


407

출력 목적으로 숫자를 반올림하려면 "%.2f"형식 문자열이 정답입니다. 그러나 실제로 추가 계산을 위해 부동 소수점 값을 반올림하려면 다음과 같이 작동합니다.

#include <math.h>

float val = 37.777779;

float rounded_down = floorf(val * 100) / 100;   /* Result: 37.77 */
float nearest = roundf(val * 100) / 100;  /* Result: 37.78 */
float rounded_up = ceilf(val * 100) / 100;      /* Result: 37.78 */

선택할 수있는 세 가지 반올림 규칙이 있습니다. 반올림 (예 : 소수점 이하 두 자리 자르기), 가장 가까운 반올림 및 반올림. 일반적으로 가장 가까운 반올림을 원합니다.

몇몇 다른 사람들이 지적했듯이, 부동 소수점 표현의 단점으로 인해이 둥근 값은 정확히 "명백한"소수 값이 아닐 수도 있지만 매우 가깝습니다.

반올림에 대한 자세한 내용, 특히 가장 가까운 반올림 규칙에 대한 자세한 내용은 반올림에 대한 Wikipedia 기사를 참조하십시오 .


4
임의의 정밀도로 반올림을 지원하도록 수정할 수 있습니까?

1
@slater '임의 정밀도'라고 말할 때 소수점 이하 두 자리 대신 세 자리로 반올림하거나 무제한 정밀도 10 진수 값을 구현하는 라이브러리를 사용해야합니까? 전자의 경우, 내가 원하는 것을 상수 100으로 명백하게 조정하십시오. 그렇지 않으면 사용하는 다중 정밀도 라이브러리를 사용하여 위에 표시된 것과 정확히 동일한 계산을 수행하십시오.
Dale Hagglund

2
@DaleHagglung 전자, 감사합니다. 100을 pow (10, (int) desiredPrecision)로 바꾸는 조정입니까?

3
네. 소수점 이하 k 자리를 반올림하려면 10 ^ k의 배율을 사용하십시오. 이것은 소수의 소수 값을 수동으로 작성하고 10의 배수로 재생하는지 쉽게 알 수 있어야합니다. 값 1.23456789로 작업하고 소수점 이하 3 자리로 반올림한다고 가정하십시오. 사용 가능한 작업은 integer반올림됩니다 . 그렇다면 소수점 왼쪽에 있도록 처음 세 자리를 어떻게 이동합니까? 10 ^ 3을 곱한 것이 분명하기를 바랍니다. 이제 그 값을 정수로 반올림 할 수 있습니다. 다음으로, 10 ^ 3으로 나누어 하위 3 자리를 다시 넣습니다.
데일 해글 룬드

1
doubles어떻게 든 이 작업을 할 수 있습니까 ? 내가 원하는 일을하지 않는 것 같습니다 :( (using floorand ceil).
아무도

87

printf에서 % .2f 사용 소수점 2 자리 만 인쇄합니다.

예:

printf("%.2f", 37.777779);

산출:

37.77

정밀도 손실이 없기 때문에이 방법이 더 좋습니다.
앨버트

2
@albert 또한 오버플 float로로 val * 100인해 범위 손실이 없다는 장점이 있습니다 .
chux-복원 Monica Monica

42

인쇄 가치를 반올림한다고 가정하면 Andrew ColesonAraK 의 대답이 맞습니다.

printf("%.2f", 37.777779);

그러나 내부 사용을 위해 숫자를 정확히 37.78로 반올림하려는 경우 (예 : 다른 값과 비교) 부동 소수점 숫자의 작동 방식으로 인해 좋은 생각이 아닙니다. 부동 소수점에 대해 동등 비교를 수행하려면 목표 값 +/- 시그마 값을 사용하십시오. 또는 숫자를 알려진 정밀도로 문자열로 인코딩하고 비교하십시오.

Greg Hewgill의 관련 질문에 대한 답변 링크를 참조하십시오. 여기에는 재무 계산에 부동 소수점을 사용하지 않아야하는 이유도 포함됩니다.


1
질문 뒤에있는 질문 (또는 질문 뒤에있는 질문)에 대해 언급했습니다. 오히려 중요한 요점입니다.
Brooks Moses

실제로 37.78은 부동 소수점으로 정확하게 표현 될 수 있습니다. Float에는 11 자리에서 12 자리의 숫자가 있습니다. 3778 377.8 또는 모든 종류의 4 자리 10 진수를 처리하기에 충분해야합니다.
Anonymous White

@HaryantoCiu 그래, 충분히 공정, 나는 내 답변을 약간 편집했습니다.
John Carter

동적 정밀도 :printf("%.*f", (int)precision, (double)number);
Minhas Kamal

24

이건 어때요:

float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);

4
-1 : a) 음수에는 적용되지 않습니다 (예, 양수이지만 여전히 그렇습니다). b) 부동 소수점에 정확한 소수점 값을 저장하는 것이 불가능하다는 언급은 없습니다
John Carter

32
@therefromhere : (a) 당신 말이 맞습니다 (b) 이것이 무엇입니까? 고등학교 시험?
Daniil

1
왜 0.5를 추가 했습니까?
muhammad tayyab 2016 년

1
반올림 규칙을 따라야합니다.
Daniil

1
@Daniil 주석의 문맥에서 반올림 규칙가장 가까운 반올림입니다
Shmil The Cat

20
printf("%.2f", 37.777779);

C- 문자열에 쓰려면 :

char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);

@Sinan : 왜 편집해야합니까? @AraK : 아니요, 크기를 잘 처리 해야합니다 :). snprintf ()를 사용하십시오.
aib

1
@aib : / ** /이 C 스타일 주석이고 질문에 C 태그가 붙어 있기 때문에 추측합니다.
Michael Haren

5
C89는 / ** /-style 만 허용했고 C99는 //-스타일에 대한 지원을 도입했습니다. lame / old 컴파일러를 사용하거나 강제 C89 모드를 사용하면 // 스타일을 사용할 수 없습니다. 2009 년에 C와 C ++ 스타일을 모두 고려해 봅시다.
앤드류 콜슨

11

반올림을 표현할 수 없기 때문에 a float를 다른 float것으로 반올림 하는 방법은 없습니다 float(부동 소수점 수의 제한). 예를 들어 37.777779를 37.78로 반올림하지만 가장 가까운 숫자는 37.781입니다.

그러나 형식 문자열 함수를 사용하여 a를 "둥글게" 할 수 있습니다float .


3
이것은 정확히 두 가지 결과를 표현할 수 없기 때문에 두 개의 float을 나누고 float를 얻는 방법이 없다고 말하는 것과 다르지 않습니다. 플로트는 덧셈과 같은 기본 요소라도 항상 정확하지 않습니다. 항상 당신이 실제로 얻는 것은 "정확한 반올림 된 답에 가장 가까운 플로트"라고 가정합니다.
Brooks Moses

내가 의미하는 것은 float소수점 이하 10 자릿수를 반올림 할 수 없으며 결과에는 항상 소수점 이하 자릿수가 n 자라는 것을 의미합니다. 당신은 여전히 float예상 한 것이 아닌을 얻습니다 .
앤드류 키튼

9

또한 C ++를 사용하는 경우 다음과 같은 함수를 만들 수 있습니다.

string prd(const double x, const int decDigits) {
    stringstream ss;
    ss << fixed;
    ss.precision(decDigits); // set # places after decimal
    ss << x;
    return ss.str();
}

할 수 있습니다 후 어떤 이중 출력 myDoublen이와 같은 코드로 소수점 이후 장소 :

std::cout << prd(myDouble,n);

7

여전히 사용할 수 있습니다 :

float ceilf(float x); // don't forget #include <math.h> and link with -lm.

예:

float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;

이것은 소수점에서 잘리고 (즉 37을 생성 할 것) 소수점 다음 두 자리로 반올림해야합니다 .
Pavel Minaev

소수점 이하 두 자리로 반올림하는 것은 사소한 변형이지만 (그러나 여전히 대답에 언급해야합니다; ZeroCool, 편집을 추가하고 싶습니까?) : float roundedValue = ceilf (valueToRound * 100.0) / 100.0;
Brooks Moses

수면 상태로 :)
ZeroCool

이 솔루션이 더 인기가없는 이유는 무엇입니까? 이것은 최소한의 코드로 정확히 어떻게 작동해야합니다. 그것에 약간의주의가 있습니까?
Andy

7

C ++ (또는 C 스타일 캐스트가있는 C)에서 함수를 작성할 수 있습니다.

/* Function to control # of decimal places to be output for x */
double showDecimals(const double& x, const int& numDecimals) {
    int y=x;
    double z=x-y;
    double m=pow(10,numDecimals);
    double q=z*m;
    double r=round(q);

    return static_cast<double>(y)+(1.0/m)*r;
}

그런 다음 std::cout << showDecimals(37.777779,2);37.78을 생산합니다.

분명히 그 함수에서 5 개의 변수를 모두 만들 필요는 없지만 논리를 볼 수 있도록 변수를 그대로 둡니다. 아마도 더 간단한 해결책이 있지만 이것은 특히 나에게 효과적입니다. 특히 소수점 이하 자릿수를 필요에 따라 조정할 수 있기 때문입니다.


5

이를 위해 항상 printf기능 군을 사용하십시오 . 값을 부동 소수점 snprintf으로 가져 오더라도 반올림 된 값을 문자열로 가져 와서 다시 구문 분석하는 것이 가장 좋습니다 atof.

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

double dround(double val, int dp) {
    int charsNeeded = 1 + snprintf(NULL, 0, "%.*f", dp, val);
    char *buffer = malloc(charsNeeded);
    snprintf(buffer, charsNeeded, "%.*f", dp, val);
    double result = atof(buffer);
    free(buffer);
    return result;
}

현재 최고 투표 응답 과 몇 가지 다른 방법-100을 곱하고 가장 가까운 정수로 반올림 한 다음 다시 100으로 나누는 접근법 이 두 가지 방식으로 결함 이 있기 때문에 이것을 말합니다 .

  • 일부 값의 경우 부동 소수점 숫자의 부정확성으로 인해 100을 곱하면 반올림 방향을 결정하는 10 진수가 4에서 5로 또는 그 반대로 바뀌기 때문에 잘못된 방향으로 반올림됩니다.
  • 일부 값의 경우 100을 곱한 다음 나누는 것은 왕복하지 않습니다. 반올림이 발생하지 않아도 최종 결과가 잘못됨을 의미합니다.

반올림 방향이 잘못되었다는 첫 번째 종류의 오류를 설명하려면이 프로그램을 실행 해보십시오.

int main(void) {
    // This number is EXACTLY representable as a double
    double x = 0.01499999999999999944488848768742172978818416595458984375;

    printf("x: %.50f\n", x);

    double res1 = dround(x, 2);
    double res2 = round(100 * x) / 100;

    printf("Rounded with snprintf: %.50f\n", res1);
    printf("Rounded with round, then divided: %.50f\n", res2);
}

이 출력이 표시됩니다.

x: 0.01499999999999999944488848768742172978818416595459
Rounded with snprintf: 0.01000000000000000020816681711721685132943093776703
Rounded with round, then divided: 0.02000000000000000041633363423443370265886187553406

우리가 시작한 값은 0.015보다 작으므로 소수점 이하 두 자리로 반올림 할 때 수학적으로 정답은 0.01입니다. 물론 0.01은 정확히 두 배로 표현할 수 없지만 결과는 0.01에 가장 가까운 두 배가 될 것으로 예상합니다. 를 사용 snprintf하면 결과가 나오지만 round(100 * x) / 1000.02를 사용 하면 잘못됩니다. 왜? 때문에 100 * x우리에게 결과와 정확히 1.5을 제공합니다. 100을 곱하면 올바른 방향으로 반올림됩니다.

설명하기 위해 두 번째 오류의 종류 - 결과는 때때로 인해 잘못 인 * 100/ 100진정으로 서로의 역수 고 - 우리가 매우 큰 숫자와 비슷한 운동을 할 수 있습니다 :

int main(void) {
    double x = 8631192423766613.0;

    printf("x: %.1f\n", x);

    double res1 = dround(x, 2);
    double res2 = round(100 * x) / 100;

    printf("Rounded with snprintf: %.1f\n", res1);
    printf("Rounded with round, then divided: %.1f\n", res2);
}

우리의 숫자는 이제 소수 부분조차도 없습니다. 정수 값이며 type과 함께 저장됩니다 double. 반올림 한 결과는 처음 시작했던 숫자와 같아야합니다.

위 프로그램을 실행하면 다음이 표시됩니다.

x: 8631192423766613.0
Rounded with snprintf: 8631192423766613.0
Rounded with round, then divided: 8631192423766612.0

죄송합니다. 우리의 snprintf방법은 올바른 결과를 다시 반환하지만, 곱셈과 그다음에 나누기 접근법은 실패합니다. 의 수학적으로 정확한 값이 때문이다 8631192423766613.0 * 100, 863119242376661300.0하지 double로 정확하게 표현할 수있다; 가장 가까운 값은 863119242376661248.0입니다. 다시 100으로 나누면 8631192423766612.0시작한 것과 다른 숫자가됩니다.

roundf여러 소수점 자릿수로 반올림 하는 데 사용 하는 것이 잘못되어 snprintf대신 사용해야한다는 충분한 데모입니다 . 그것이 당신에게 끔찍한 해킹처럼 느껴지면 아마도 CPython이 기본적으로하는 일 이라는 지식으로 안심할 수 있습니다 .


IEEE 부동 소수점의 기이함과 직접적인 대안을 제공하여 내 대답과 비슷한 질문에 대한 구체적인 예를 보려면 +1하십시오. 나는 주변에서 매우 오래 전에 인쇄와 친구들에게 많은 노력을 기울여 주변을 떠 다니는 부동 소수점 값에 안전하다는 것을 알고있었습니다. 그때 한 작업이 여기에 표시 될 수 있습니다.
Dale Hagglund

Ahem ... 저쪽 끝에 샐러드라는 단어가있어서 죄송합니다. 이제 편집하기에 너무 늦었습니다. 내가 말하려는 것은 "... printf와 친구들을 안전하게 만들기 위해 많은 노력을 기울였습니다"
Dale Hagglund

4

사용하십시오 float roundf(float x).

"라운드 함수는 현재 반올림 방향에 관계없이 반값을 0에서 반올림하여 인수를 부동 소수점 형식으로 가장 가까운 정수 값으로 반올림합니다." C11dr §7.12.9.5

#include <math.h>
float y = roundf(x * 100.0f) / 100.0f; 

float구현 에 따라 절반으로 보이는 숫자는 그렇지 않습니다. 부동 소수점은 일반적으로 밑이 2입니다. 또한, 0.01모든 "반쯤"경우에 가장 가까운 반올림을 정확하게 반올림하는 것이 가장 어렵습니다.

void r100(const char *s) {
  float x, y;
  sscanf(s, "%f", &x);
  y = round(x*100.0)/100.0;
  printf("%6s %.12e %.12e\n", s, x, y);
}

int main(void) {
  r100("1.115");
  r100("1.125");
  r100("1.135");
  return 0;
}

 1.115 1.115000009537e+00 1.120000004768e+00  
 1.125 1.125000000000e+00 1.129999995232e+00
 1.135 1.134999990463e+00 1.139999985695e+00

"1.115"하지만로 변환 1.11와 1.12 사이 "절반 방법"입니다 float값이 없다, 1.115000009537...그리고 "반 방법은"더 이상이지만, 가까운 1.12 및 라운드에 가장 가까운에 float1.120000004768...

"1.125"는 1.12와 1.13 사이의 "반쯤"이며로 변환 될 때 float값은 정확히 1.125"반쪽"입니다. 이 때문에 가장 가까운 심지어 규칙에 대한 관계 및 라운드에 1.13으로 반올림 float1.129999995232...

"1.135"하지만로 변환 1.13와 1.14 사이 "절반 방법"입니다 float값이 없다, 1.134999990463...그리고 "반 방법은"더 이상이지만, 가까운 1.13 및 라운드에 가장 가까운에 float1.129999995232...

코드가 사용 된 경우

y = roundf(x*100.0f)/100.0f;

"1.135"는 변환 1.13와 1.14 사이의 "절반 방법"이지만 float, 값은 없습니다 1.134999990463...와 "절반 방법은"더 이상이지만, 가까이에 1.13하지만 잘못 에 라운드 float1.139999985695...의 더 제한된 정밀도에 의한 floatdouble. 이 잘못된 값은 코딩 목표에 따라 올바른 것으로 간주 될 수 있습니다.


4

부동 소수점 숫자를 반올림하기 위해이 매크로를 만들었습니다. 헤더 / 파일에 추가하십시오.

#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))

예를 들면 다음과 같습니다.

float x = ROUNDF(3.141592, 100)

x는 3.14와 같습니다. :)


이것은 잘리지 만 질문은 반올림을 요청합니다. 또한 부동 소수점 연산에서 반올림 오류가 발생할 수 있습니다.
에릭 포스트 피쉴

3
double f_round(double dval, int n)
{
    char l_fmtp[32], l_buf[64];
    char *p_str;
    sprintf (l_fmtp, "%%.%df", n);
    if (dval>=0)
            sprintf (l_buf, l_fmtp, dval);
    else
            sprintf (l_buf, l_fmtp, dval);
    return ((double)strtod(l_buf, &p_str));

}

여기에 n소수의 수입니다

예:

double d = 100.23456;

printf("%f", f_round(d, 4));// result: 100.2346

printf("%f", f_round(d, 2));// result: 100.23

-1의 4 가지 이유 : 1) 설명 부족, 2) 버퍼 오버 플로우 취약점-오버플로가 발생하여 크면 크래시가 발생할 수 있습니다. dval3) 각 분기에서 정확히 같은 일을 하는 이상한 if/ else블록 및 4) sprintf제 2 sprintf호출을 위한 포맷 지정자를 구축하기위한 지나치게 복잡한 사용 .*동일한 sprintf호출 에 대한 인수로 double 값과 소수 자릿수를 사용 하고 전달하는 것이 더 간단합니다 .
Mark Amery

3

코드 정의 :

#define roundz(x,d) ((floor(((x)*pow(10,d))+.5))/pow(10,d))

결과 :

a = 8.000000
sqrt(a) = r = 2.828427
roundz(r,2) = 2.830000
roundz(r,3) = 2.828000
roundz(r,5) = 2.828430

0

먼저이 질문에 또 다른 답을 추가 한 이유를 정당화하려고합니다. 이상적인 세계에서 반올림은 실제로 큰 문제가 아닙니다. 그러나 실제 시스템에서는 반올림으로 인해 예상치 못한 반올림이 발생할 수있는 몇 가지 문제에 대처해야 할 수도 있습니다. 예를 들어, 최종 결과가 반올림되고 소수점 2 자리로 사용자에게 표시되는 재무 계산을 수행 할 수 있습니다. 이 같은 값은 소수점 이하 두 자리 이상을 포함 할 수있는 데이터베이스에 고정 된 정밀도로 저장됩니다 (다양한 이유로; 유지할 최적의 장소가 없습니다 ... 각 시스템이 지원해야하는 특정 상황에 따라 다릅니다 (예 : 가격이 작은 품목) 단위당 페니의 분율); 및 결과가 플러스 / 마이너스 엡실론 인 값에 대해 수행되는 부동 소수점 계산. 나는 이러한 문제에 직면 해 왔으며 수년에 걸쳐 나만의 전략을 발전시켜 왔습니다. 나는 모든 시나리오에 직면했거나 최선의 대답을 가지고 있다고 주장하지는 않지만 아래는 이러한 문제를 극복하는 지금까지의 접근 방식의 예입니다.

소수점 이하 6 자리는 다음 반올림 함수 / 방법을 사용하여 float / doubles (특정 응용 프로그램에 대한 임의의 결정) 계산에 충분한 정밀도로 간주됩니다.

double Round(double x, int p)
{
    if (x != 0.0) {
        return ((floor((fabs(x)*pow(double(10.0),p))+0.5))/pow(double(10.0),p))*(x/fabs(x));
    } else {
        return 0.0;
    }
}

결과를 표시하기 위해 소수점 이하 2 자리로 반올림하는 방법은 다음과 같습니다.

double val;
// ...perform calculations on val
String(Round(Round(Round(val,8),6),2));

에 대한 val = 6.825결과는6.83 예상대로입니다.

에 대한 val = 6.824999결과는 6.82입니다. 여기서는 계산 결과가 정확히6.824999 소수점 7 자리는 0입니다.

에 대한 val = 6.8249999결과는 6.83입니다. 9이 경우 소수점 7 자리 는 Round(val,6)함수가 예상 결과를 제공합니다. 이 경우 후행이 얼마든지있을 수 있습니다.9 .

에 대한 val = 6.824999499999결과는 6.83입니다. 첫 번째 단계로 소수점 8 자리로 반올림 Round(val,8)하면 계산 된 부동 소수점 결과가로 계산 6.8249995되지만 내부적으로6.824999499999... 합니다.

마지막으로 질문의 예는 다음 val = 37.777779과 같습니다.37.78 .

이 접근법은 다음과 같이 더 일반화 될 수 있습니다.

double val;
// ...perform calculations on val
String(Round(Round(Round(val,N+2),N),2));

여기서 N은 float / doubles에 대한 모든 중간 계산에 대해 유지되는 정밀도입니다. 이것은 음수 값에서도 작동합니다. 이 접근법이 모든 가능성에 대해 수학적으로 올바른지 모르겠습니다.



-1

... 또는 라이브러리없이 구식으로 할 수 있습니다.

float a = 37.777779;

int b = a; // b = 37    
float c = a - b; // c = 0.777779   
c *= 100; // c = 77.777863   
int d = c; // d = 77;    
a = b + d / (float)100; // a = 37.770000;

물론 번호에서 추가 정보를 제거하려는 경우에도 마찬가지입니다.


-2

이 함수는 숫자와 정밀도를 취하고 반올림 한 숫자를 반환합니다.

float roundoff(float num,int precision)
{
      int temp=(int )(num*pow(10,precision));
      int num1=num*pow(10,precision+1);
      temp*=10;
      temp+=5;
      if(num1>=temp)
              num1+=10;
      num1/=10;
      num1*=10;
      num=num1/pow(10,precision+1);
      return num;
}

소수점을 왼쪽으로 이동하고 5보다 큰 조건을 확인하여 부동 소수점 숫자를 int로 변환합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.