Programming

C에서 소수점 이하 2 자리로 반올림

procodes 2020. 5. 10. 11:55
반응형

C에서 소수점 이하 2 자리로 반올림


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 */

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

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

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


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

예:

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

산출:

37.77

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

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

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

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


이건 어때요:

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 .


또한 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);

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

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


사용하십시오 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...

Although "1.135" is "half-way" between 1.13 and 1.14, when converted to float, the value is 1.134999990463... and is no longer "half-way", but closer to 1.13 and rounds to the closest float of 1.129999995232...

If code used

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

Although "1.135" is "half-way" between 1.13 and 1.14, when converted to float, the value is 1.134999990463... and is no longer "half-way", but closer to 1.13 but incorrectly rounds to float of 1.139999985695... due to the more limited precision of float vs. double. This incorrect value may be viewed as correct, depending on coding goals.


이를 위해 항상 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을 곱한 다음 나누는 것은 왕복하지 않습니다. 반올림이 발생하지 않아도 최종 결과가 잘못됨을 의미합니다.

To illustrate the first kind of error - the rounding direction sometimes being wrong - try running this program:

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

You'll see this output:

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

Note that the value we started with was less than 0.015, and so the mathematically correct answer when rounding it to 2 decimal places is 0.01. Of course, 0.01 is not exactly representable as a double, but we expect our result to be the double nearest to 0.01. Using snprintf gives us that result, but using round(100 * x) / 100 gives us 0.02, which is wrong. Why? Because 100 * x gives us exactly 1.5 as the result. Multiplying by 100 thus changes the correct direction to round in.

To illustrate the second kind of error - the result sometimes being wrong due to * 100 and / 100 not truly being inverses of each other - we can do a similar exercise with a very big number:

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

Our number now doesn't even have a fractional part; it's an integer value, just stored with type double. So the result after rounding it should be the same number we started with, right?

If you run the program above, you'll see:

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

Oops. Our snprintf method returns the right result again, but the multiply-then-round-then-divide approach fails. That's because the mathematically correct value of 8631192423766613.0 * 100, 863119242376661300.0, is not exactly representable as a double; the closest value is 863119242376661248.0. When you divide that back by 100, you get 8631192423766612.0 - a different number to the one you started with.

Hopefully that's a sufficient demonstration that using roundf for rounding to a number of decimal places is broken, and that you should use snprintf instead. If that feels like a horrible hack to you, perhaps you'll be reassured by the knowledge that it's basically what CPython does.


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

}

Here n is the number of decimals

example:

double d = 100.23456;

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

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

I made this macro for rounding float numbers. Add it in your header / being of file

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

Here is an example:

float x = ROUNDF(3.141592, 100)

x equals 3.14 :)


Code definition :

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

Results :

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

Let me first attempt to justify my reason for adding yet another answer to this question. In an ideal world, rounding is not really a big deal. However, in real systems, you may need to contend with several issues that can result in rounding that may not be what you expect. For example, you may be performing financial calculations where final results are rounded and displayed to users as 2 decimal places; these same values are stored with fixed precision in a database that may include more than 2 decimal places (for various reasons; there is no optimal number of places to keep...depends on specific situations each system must support, e.g. tiny items whose prices are fractions of a penny per unit); and, floating point computations performed on values where the results are plus/minus epsilon. I have been confronting these issues and evolving my own strategy over the years. I won't claim that I have faced every scenario or have the best answer, but below is an example of my approach so far that overcomes these issues:

Suppose 6 decimal places is regarded as sufficient precision for calculations on floats/doubles (an arbitrary decision for the specific application), using the following rounding function/method:

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

Rounding to 2 decimal places for presentation of a result can be performed as:

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

For val = 6.825, result is 6.83 as expected.

For val = 6.824999, result is 6.82. Here the assumption is that the calculation resulted in exactly 6.824999 and the 7th decimal place is zero.

For val = 6.8249999, result is 6.83. The 7th decimal place being 9 in this case causes the Round(val,6) function to give the expected result. For this case, there could be any number of trailing 9s.

For val = 6.824999499999, result is 6.83. Rounding to the 8th decimal place as a first step, i.e. Round(val,8), takes care of the one nasty case whereby a calculated floating point result calculates to 6.8249995, but is internally represented as 6.824999499999....

Finally, the example from the question...val = 37.777779 results in 37.78.

This approach could be further generalized as:

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

where N is precision to be maintained for all intermediate calculations on floats/doubles. This works on negative values as well. I do not know if this approach is mathematically correct for all possibilities.


...or you can do it the old-fashioned way without any libraries:

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;

That of course if you want to remove the extra information from the number.


this function takes the number and precision and returns the rounded off number

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

it converts the floating point number into int by left shifting the point and checking for the greater than five condition.

참고URL : https://stackoverflow.com/questions/1343890/rounding-number-to-2-decimal-places-in-c

반응형