C에서 부동 소수점 값 (예 : 37.777779)을 소수점 이하 두 자리 (37.78)로 반올림하려면 어떻게해야합니까?
C에서 부동 소수점 값 (예 : 37.777779)을 소수점 이하 두 자리 (37.78)로 반올림하려면 어떻게해야합니까?
답변:
출력 목적으로 숫자를 반올림하려면 "%.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 기사를 참조하십시오 .
doubles
어떻게 든 이 작업을 할 수 있습니까 ? 내가 원하는 일을하지 않는 것 같습니다 :( (using floor
and ceil
).
printf에서 % .2f 사용 소수점 2 자리 만 인쇄합니다.
예:
printf("%.2f", 37.777779);
산출:
37.77
float
로로 val * 100
인해 범위 손실이 없다는 장점이 있습니다 .
인쇄 가치를 반올림한다고 가정하면 Andrew Coleson 과 AraK 의 대답이 맞습니다.
printf("%.2f", 37.777779);
그러나 내부 사용을 위해 숫자를 정확히 37.78로 반올림하려는 경우 (예 : 다른 값과 비교) 부동 소수점 숫자의 작동 방식으로 인해 좋은 생각이 아닙니다. 부동 소수점에 대해 동등 비교를 수행하려면 목표 값 +/- 시그마 값을 사용하십시오. 또는 숫자를 알려진 정밀도로 문자열로 인코딩하고 비교하십시오.
Greg Hewgill의 관련 질문에 대한 답변 링크를 참조하십시오. 여기에는 재무 계산에 부동 소수점을 사용하지 않아야하는 이유도 포함됩니다.
printf("%.*f", (int)precision, (double)number);
이건 어때요:
float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);
printf("%.2f", 37.777779);
C- 문자열에 쓰려면 :
char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);
반올림을 표현할 수 없기 때문에 a float
를 다른 float
것으로 반올림 하는 방법은 없습니다 float
(부동 소수점 수의 제한). 예를 들어 37.777779를 37.78로 반올림하지만 가장 가까운 숫자는 37.781입니다.
그러나 형식 문자열 함수를 사용하여 a를 "둥글게" 할 수 있습니다float
.
float
소수점 이하 10 자릿수를 반올림 할 수 없으며 결과에는 항상 소수점 이하 자릿수가 n 자라는 것을 의미합니다. 당신은 여전히 float
예상 한 것이 아닌을 얻습니다 .
여전히 사용할 수 있습니다 :
float ceilf(float x); // don't forget #include <math.h> and link with -lm.
예:
float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;
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 개의 변수를 모두 만들 필요는 없지만 논리를 볼 수 있도록 변수를 그대로 둡니다. 아마도 더 간단한 해결책이 있지만 이것은 특히 나에게 효과적입니다. 특히 소수점 이하 자릿수를 필요에 따라 조정할 수 있기 때문입니다.
이를 위해 항상 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으로 나누는 접근법 이 두 가지 방식으로 결함 이 있기 때문에 이것을 말합니다 .
반올림 방향이 잘못되었다는 첫 번째 종류의 오류를 설명하려면이 프로그램을 실행 해보십시오.
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) / 100
0.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이 기본적으로하는 일 이라는 지식으로 안심할 수 있습니다 .
사용하십시오 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 및 라운드에 가장 가까운에 float
의1.120000004768...
"1.125"는 1.12와 1.13 사이의 "반쯤"이며로 변환 될 때 float
값은 정확히 1.125
"반쪽"입니다. 이 때문에 가장 가까운 심지어 규칙에 대한 관계 및 라운드에 1.13으로 반올림 float
의1.129999995232...
"1.135"하지만로 변환 1.13와 1.14 사이 "절반 방법"입니다 float
값이 없다, 1.134999990463...
그리고 "반 방법은"더 이상이지만, 가까운 1.13 및 라운드에 가장 가까운에 float
의1.129999995232...
코드가 사용 된 경우
y = roundf(x*100.0f)/100.0f;
"1.135"는 변환 1.13와 1.14 사이의 "절반 방법"이지만 float
, 값은 없습니다 1.134999990463...
와 "절반 방법은"더 이상이지만, 가까이에 1.13하지만 잘못 에 라운드 float
의 1.139999985695...
의 더 제한된 정밀도에 의한 float
대 double
. 이 잘못된 값은 코딩 목표에 따라 올바른 것으로 간주 될 수 있습니다.
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
dval
3) 각 분기에서 정확히 같은 일을 하는 이상한 if
/ else
블록 및 4) sprintf
제 2 sprintf
호출을 위한 포맷 지정자를 구축하기위한 지나치게 복잡한 사용 .*
동일한 sprintf
호출 에 대한 인수로 double 값과 소수 자릿수를 사용 하고 전달하는 것이 더 간단합니다 .
먼저이 질문에 또 다른 답을 추가 한 이유를 정당화하려고합니다. 이상적인 세계에서 반올림은 실제로 큰 문제가 아닙니다. 그러나 실제 시스템에서는 반올림으로 인해 예상치 못한 반올림이 발생할 수있는 몇 가지 문제에 대처해야 할 수도 있습니다. 예를 들어, 최종 결과가 반올림되고 소수점 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에 대한 모든 중간 계산에 대해 유지되는 정밀도입니다. 이것은 음수 값에서도 작동합니다. 이 접근법이 모든 가능성에 대해 수학적으로 올바른지 모르겠습니다.
이 함수는 숫자와 정밀도를 취하고 반올림 한 숫자를 반환합니다.
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로 변환합니다.
float
(및double
)은 십진 부동 소수점이 아니기 때문에 이진 부동 소수점 이기 때문에 숫자 자체를 올바르게 반올림 할 수 없으므로 소수 자릿수로 반올림하는 것은 의미가 없습니다. 그러나 출력을 반올림 할 수 있습니다.