Math.Round (2.5)가 3 대신 2를 반환하는 이유는 무엇입니까?


415

C #에서 결과 Math.Round(2.5)는 2입니다.

3으로되어 있지 않습니까? C #에서 왜 2입니까?


5
실제로는 기능입니다. <a href=" msdn.microsoft.com/en-us/library/… MSDN 문서</a>를 참조하십시오 이러한 종류의 반올림은 은행가 반올림으로 알려져 있으며, 해결 방법은 <a href = " msdn이 있습니다. 호출자가 반올림을 수행하는 방법을 지정할 수있는 microsoft.com/en-us/library/… 과부하 </a>
Joe

1
분명히 round 메서드는 두 정수 사이의 숫자를 정확하게 반올림하도록 요청하면 짝수 정수를 반환합니다. 따라서 Math.Round (3.5)는 4를 반환합니다. 이 기사 참조
Matthew Jones

20
Math.Round(2.5, 0, MidpointRounding.AwayFromZero);
Robert Durgin 2016 년

SQL Server는 그런 식으로 반올림합니다. T-SQL에서 수행 된 C # 단위 테스트 ti validate 반올림이있을 때 흥미로운 테스트 결과.
idstam 2016 년

7
@ amed는 버그가 아닙니다. 이진 부동 소수점이 작동하는 방식입니다. 1.005정확히 두 배로 표현할 수 없습니다. 아마입니다 1.00499.... Decimal이 문제 를 사용 하면 사라집니다. 두 자릿수의 십진수를 사용하는 Math.Round 과부하의 존재는 모호한 디자인 선택 IMO입니다.
코드 InChaos

답변:


560

첫째, 이것은 어쨌든 C # 버그가 아니며 .NET 버그 일 것입니다. C #은 언어 Math.Round이며 구현 방법을 결정하지 않습니다 .

그리고 둘째, 아니요-만약 당신이 읽는다면 문서 기본 반올림이 "짝수로 반올림"(은행가 반올림)임을 알 수 있습니다.

반환 값
유형 : System.Double
가장 가까운 정수 a. a의 분수 성분이 두 정수 사이의 중간 인 경우, 하나는 짝수이고 다른 정수는 짝수입니다. 이 메소드는 Double정수 타입 대신에 리턴합니다 .

비고
이 방법의 동작은 IEEE 표준 754, 섹션 4를 따릅니다. 이러한 종류의 반올림은 때때로 가장 가까운 반올림 또는 뱅커의 반올림이라고합니다. 중간 지점 값을 한 방향으로 일관되게 반올림하여 발생하는 반올림 오류를 최소화합니다.

값 을 취하는 과부하Math.Round사용하여 중간 점을 반올림 하는 방법 을 지정할 수 있습니다 . 하나의 과부하가 있으며 각 과부하에는 해당하지 않습니다.MidpointRoundingMidpointRounding

이 불이행이 잘 선택되었는지 여부는 다른 문제입니다. ( MidpointRounding.NET 2.0에서만 도입되었습니다. 그 전에는 원하는 동작을 직접 수행하지 않고도 구현할 수있는 쉬운 방법이 있는지 확실하지 않습니다.) 특히, 역사는 예상 하지 않은 것으로 나타났습니다 동작 대부분의 경우 API 디자인의 기본적인 죄. Banker 's Rounding이 유용한 이유를 알 수 있지만 여전히 많은 사람들에게 놀라운 일입니다.

RoundingMode더 많은 옵션을 제공 하는 가장 가까운 Java 등가 열거 형 ( )을 살펴 보는 것이 좋습니다 . (중간 지점 만 다루지는 않습니다.)


4
나는 이것이 버그인지 알지 못한다. 나는 .5가 가장 가까운 가장 높은 정수만큼 가장 가까운 가장 낮은 정수에 가깝기 때문에 의도적으로 설계된 것이라고 생각한다.
Stan R.

3
.NET이 적용되기 전에 VB 에서이 동작을 기억합니다.
John Fiala 2016 년

7
실제로 IEEE 표준 754, 섹션 4는 설명서에 나와 있습니다.
Jon Skeet

2
나는 얼마전에 이것에 화상을 입었고, 그것이 너무나 미치광이라고 생각했다. 다행히 그들은 우리 모두가 초등학교에서 배운 반올림을 지정하는 방법을 추가했습니다. MidPointRounding.
Shea

26
+1 "예상치 않은 행동입니다 [...] API 디자인의 주요 죄악"
BlueRaja-Danny Pflughoeft

215

이것을 짝수로 반올림 (또는 은행가의 반올림)이라고하며, 이는 발생하는 누적 오류를 최소화하기위한 유효한 반올림 전략입니다 (MidpointRounding.ToEven). 당신은 항상 라운드 같은 방향으로 0.5 수, 오류가 빠른 (심지어 라운드에-것을 최소화하도록되어) 발생한다면이 이론은, 그 인 (A)를 .

다음에 대한 MSDN 설명을 보려면 다음 링크를 따르십시오.

  • Math.Floor음의 무한대로 반올림합니다.
  • Math.Ceiling양의 무한대로 반올림합니다.
  • Math.Truncate0으로 올림 또는 내림합니다.
  • Math.Round가장 가까운 정수 또는 지정된 소수 자릿수로 반올림합니다. 마지막 자리가 고르거나 ( " Round(2.5,MidpointRounding.ToEven)"2가되거나) 0에서 멀어 지도록 ( " Round(2.5,MidpointRounding.AwayFromZero)"3이 됨) 반올림과 같이 두 가능성 사이에서 정확히 같은 거리에있을 경우 동작을 지정할 수 있습니다 .

다음 다이어그램과 표가 도움이 될 수 있습니다.

-3        -2        -1         0         1         2         3
 +--|------+---------+----|----+--|------+----|----+-------|-+
    a                     b       c           d            e

                       a=-2.7  b=-0.5  c=0.3  d=1.5  e=2.8
                       ======  ======  =====  =====  =====
Floor                    -3      -1      0      1      2
Ceiling                  -2       0      1      2      3
Truncate                 -2       0      0      1      2
Round(ToEven)            -3       0      0      2      3
Round(AwayFromZero)      -3      -1      0      2      3

참고 Round가 소수점의 특정 번호로 반올림 할 수 있기 때문에 단순히, 그것은 것보다 훨씬 더 강력하다. 다른 모든 것들은 항상 제로 소수로 반올림합니다. 예를 들면 다음과 같습니다.

n = 3.145;
a = System.Math.Round (n, 2, MidpointRounding.ToEven);       // 3.14
b = System.Math.Round (n, 2, MidpointRounding.AwayFromZero); // 3.15

다른 함수에서는 곱하기 / 나누기 속임수를 사용해야 동일한 효과를 얻을 수 있습니다.

c = System.Math.Truncate (n * 100) / 100;                    // 3.14
d = System.Math.Ceiling (n * 100) / 100;                     // 3.15

(ㅏ) 물론이 이론은 데이터가 짝수 반 (0.5, 2.5, 4.5, ...)과 홀수 반 (1.5, 3.5, ...)에 걸쳐 상당히 균일하게 분포되어 있다는 사실에 달려 있습니다.

경우 모든 은 "반 값"(예를 들어) 고르게 있습니다 당신은 항상 반올림 것처럼 오류는 빠른 속도로 축적됩니다.


3
또한 은행의 반올림으로 알려진
Pondidum

좋은 설명입니다! 나는 오류가 어떻게 누적되는지 직접보고 싶었고 은행의 반올림을 사용하여 반올림 된 값이 장기적으로 합계와 평균이 원래 값과 훨씬 더 가깝다는 것을 보여주는 스크립트를 작성했습니다. github.com/AmadeusW/RoundingDemo (플롯의 사진 가능)
으로 Amadeusz 조렉

잠시 후 : e틱보다 2틱 (= 2.8)이 더 낫지 않아야 합니까?
superjos

기억하는 간단한 방법은 10 번째 자리를 5라고 가정합니다.-1 위와 10 위는 모두 홀수 = 반올림-1 위와 10 위는 혼합 = 반올림 * 0은 홀수입니다 * 음수는 반대로
Arkham Angel

@ArkhamAngel, 그것은 실제로 "마지막 숫자를 짝수로 만드는 것"보다 기억 하기 어려운 것 같습니다 :-)
paxdiablo

42

에서 MSDN, Math.Round (더블 A) 반환합니다 :

가장 가까운 정수 a. a의 분수 성분이 두 정수 사이의 중간에 있고, 그 중 하나는 짝수이고 다른 정수는 짝수입니다.

... 2와 3 사이의 중간 인 2.5는 짝수 (2)로 내림됩니다. 이 은행가의 반올림 이라고합니다 (또는 하며 일반적으로 사용되는 반올림 표준입니다.

동일한 MSDN 기사 :

이 방법의 동작은 IEEE 표준 754, 섹션 4를 따릅니다. 이러한 종류의 반올림은 때때로 가장 가까운 반올림 또는 뱅커의 반올림이라고합니다. 중간 지점 값을 한 방향으로 일관되게 반올림하여 발생하는 반올림 오류를 최소화합니다.

MidpointRounding모드 를 사용하는 Math.Round의 오버로드를 호출하여 다른 반올림 동작을 지정할 수 있습니다 .


37

MSDN에서 Math.Round다음을 확인해야합니다 .

이 방법의 동작은 IEEE 표준 754, 섹션 4를 따릅니다. 이러한 종류의 반올림은 때때로 가장 가까운 반올림 또는 뱅커의 반올림이라고합니다.

Math.Round오버로드 사용 동작을 지정할 수 있습니다 .

Math.Round(2.5, 0, MidpointRounding.AwayFromZero); // gives 3

Math.Round(2.5, 0, MidpointRounding.ToEven); // gives 2

31

반올림의 특성

분수를 포함하는 숫자를 정수로 반올림하는 작업을 고려하십시오. 이 상황에서 반올림하는 과정은 반올림 할 숫자를 가장 잘 나타내는 정수를 결정하는 것입니다.

일반적으로 또는 '산술'반올림에서는 2.1, 2.2, 2.3 및 2.4가 2.0으로 반올림되는 것이 분명합니다. 및 2.6, 2.7, 2.8 및 2.9 내지 3.0.

2.5는 3.0보다 2.0에 가깝지 않습니다. 2.0과 3.0 중 하나를 선택하는 것은 귀하에게 달려 있습니다.

빼기 숫자 인 경우 -2.1, -2.2, -2.3 및 -2.4는 -2.0이됩니다. -2.6, 2.7, 2.8 및 2.9는 산술 반올림에서 -3.0이됩니다.

-2.5의 경우 -2.0과 -3.0 사이에서 선택해야합니다.

다른 형태의 라운딩

'반올림'은 소수점 이하 자릿수를 사용하여 다음 '전체'숫자로 만듭니다. 따라서 2.5와 2.6은 3.0으로 반올림 할뿐만 아니라 2.1과 2.2도 반올림합니다.

반올림하면 양수와 음수가 모두 0에서 멀어집니다. 예 : 2.5 ~ 3.0 및 -2.5 ~ -3.0

'반올림'은 원치 않는 숫자를 잘라내어 숫자를 자릅니다. 이것은 숫자를 0으로 이동시키는 효과가 있습니다. 예 : 2.5 ~ 2.0 및 -2.5 ~ -2.0

가장 일반적인 형태 인 "은행가 반올림"에서 반올림 할 0.5는 반올림 또는 내림되므로 반올림 결과는 항상 짝수입니다. 따라서 2.5는 2.0, 3.5-4.0, 4.5-4.0, 5.5-6.0 등으로 반올림됩니다.

'대체 반올림'은 반올림과 반올림 사이에서 0.5의 프로세스를 번갈아 표시합니다.

'무작위 반올림'은 전적으로 무작위로 0.5를 올리거나 내립니다.

대칭과 비대칭

반올림 함수는 모든 숫자를 0에서 반올림하거나 모든 숫자를 0으로 반올림하면 '대칭'이라고합니다.

양수를 0으로 반올림하고 음수를 0에서 멀어지게하는 경우 함수는 '비대칭'입니다. 2.5 내지 2.0; 및 -2.5 내지 -3.0.

또한 비대칭은 양수를 0에서 멀어지고 음수를 0으로 반올림하는 함수입니다. 예 : 2.5 내지 3.0; 및 -2.5 내지 -2.0.

대부분의 사람들은 대칭 반올림을 생각합니다. 여기서 -2.5는 -3.0으로 반올림되고 3.5는 4.0으로 반올림됩니다. (C #에서Round(AwayFromZero))


28

기본 MidpointRounding.ToEven또는 은행원 반올림 ( 2.5는 2가되고 4.5는 4가됩니다) )은 회계 보고서를 작성하기 전에 저를 찔렀습니다. 이 게시물.

짝수로 내림하는이 은행가는 누구입니까 (영국 은행가 일 것입니다!)?

위키 백과에서

은행가 반올림이라는 용어의 기원은 여전히 ​​모호합니다. 이 반올림 방법이 뱅킹의 표준 이었다면 증거를 찾기가 매우 어려웠습니다. 반대로, 유럽위원회 보고서의 섹션 2 유로 도입 및 통화 반올림은 은행에서 반올림에 대한 표준 접근법이 없었 음을 시사합니다. "반쯤"금액을 반올림하도록 지정합니다.

물론 은행이 많은 금액의 예금을받는 데 사용하지 않는 한 특히 은행을 위해 매우 반올림하는 방법으로 보입니다. £ 2.4m를 입금하지만, £ 2m이라고합니다.

IEEE 표준 754는 1985 년으로 거슬러 올라가며 두 가지 반올림 방식을 제공하지만 표준에서 권장하는대로 은행가를 사용합니다. 이 위키 백과 기사 에는 언어가 반올림을 구현하는 방법에 대한 긴 목록이 있으며 (아래 중 하나라도 잘못된 경우 수정) 대부분 은행가를 사용하지 않고 학교에서 배운 반올림을 사용합니다.

  • math.h의 C / C ++ round ()는 0에서 반올림합니다 (뱅커의 반올림 아님)
  • Java Math.Round 는 0에서 반올림합니다 (결과를 바닥에 놓고 0.5를 더하고 정수로 캐스트). BigDecimal 에는 대안이 있습니다.
  • 은 C와 비슷한 방법을 사용한다
  • Javascript는 Java의 Math.Round와 동일합니다.

정보 주셔서 감사합니다. 나는 이것을 깨닫지 못했습니다. 수백만의 비웃음에 대한 당신의 예는 조금 비웃지 만, 당신이 센트로 반올림하더라도, 모든 센트가 반올림되면 1000 만 개의 은행 계좌에이자를 지불하면 은행에 많은 비용이 들거나 모두가 있으면 고객에게 많은 비용이 듭니다 반 센트가 반올림됩니다. 그래서 이것이 합의 된 표준이라고 상상할 수 있습니다. 이것이 실제로 은행가에 의해 사용되는지 확실하지 않습니다. 대부분의 고객은 많은 돈을 가져 오는 동안 반올림을 눈치 채지 못하지만 고객 친화적 인 법률을 가진 국가에 거주하는 경우 법에 의해 의무화 될 수 있습니다.
Harald Coppoolse

15

MSDN에서 :

기본적으로 Math.Round는 MidpointRounding.ToEven을 사용합니다. 대안은 "제로에서 반올림"이 학교에서 더 일반적으로 가르치기 때문에 대부분의 사람들은 "반올림에서 짝수로"에 익숙하지 않습니다. .NET은 "0에서 반올림"하는 경향이 반올림보다 약간 더 자주 반올림하는 경향을 공유하지 않기 때문에 통계적으로 우수하므로 "둥근 반올림"으로 기본 설정됩니다 (반올림되는 숫자가 양수인 것으로 가정). )

http://msdn.microsoft.com/en-us/library/system.math.round.aspx


3

Silverlight는 MidpointRounding 옵션을 지원하지 않으므로 직접 작성해야합니다. 다음과 같은 것 :

public double RoundCorrect(double d, int decimals)
{
    double multiplier = Math.Pow(10, decimals);

    if (d < 0)
        multiplier *= -1;

    return Math.Floor((d * multiplier) + 0.5) / multiplier;

}

이를 확장으로 사용하는 방법을 포함하는 예는 .NET 및 Silverlight Rounding 게시물을 참조하십시오.


3

C # 응용 프로그램이 아닌 동안 SQL 서버가 0.5에서 1로 반올림되는이 문제가 발생했습니다. 따라서 두 가지 다른 결과가 나타납니다.

다음은 int / long 구현입니다. 이것이 Java가 반올림하는 방법입니다.

int roundedNumber = (int)Math.Floor(d + 0.5);

아마도 당신이 생각할 수있는 가장 효율적인 방법 일 것입니다.

double을 유지하고 decimal precision을 사용하려면 소수점 이하 자릿수에 따라 10의 지수를 사용하면됩니다.

public double getRounding(double number, int decimalPoints)
{
    double decimalPowerOfTen = Math.Pow(10, decimalPoints);
    return Math.Floor(number * decimalPowerOfTen + 0.5)/ decimalPowerOfTen;
}

소수점에 음수를 입력 할 수 있으며 단어도 좋습니다.

getRounding(239, -2) = 200


0

이 게시물에는 원하는 답변이 있습니다.

http://weblogs.asp.net/sfurman/archive/2003/03/07/3537.aspx

기본적으로 이것은 다음과 같습니다.

반환 값

자릿수와 같은 정밀도로 가장 가까운 숫자입니다. 값이 두 숫자 사이의 중간 인 경우, 하나는 짝수이고 다른 하나는 홀수 인 경우 짝수가 반환됩니다. 값의 정밀도가 자릿수보다 작 으면 값이 변경되지 않은 상태로 반환됩니다.

이 방법의 동작은 IEEE 표준 754, 섹션 4를 따릅니다. 이러한 종류의 반올림은 때때로 가장 가까운 반올림 또는 뱅커의 반올림이라고합니다. 숫자가 0 인 경우, 이러한 종류의 반올림을 0으로 반올림하는 경우가 있습니다.


0

Silverlight는 MidpointRounding 옵션을 지원하지 않습니다. MidpointRounding 열거 형을 추가하는 Silverlight의 확장 방법은 다음과 같습니다.

public enum MidpointRounding
{
    ToEven,
    AwayFromZero
}

public static class DecimalExtensions
{
    public static decimal Round(this decimal d, MidpointRounding mode)
    {
        return d.Round(0, mode);
    }

    /// <summary>
    /// Rounds using arithmetic (5 rounds up) symmetrical (up is away from zero) rounding
    /// </summary>
    /// <param name="d">A Decimal number to be rounded.</param>
    /// <param name="decimals">The number of significant fractional digits (precision) in the return value.</param>
    /// <returns>The number nearest d with precision equal to decimals. If d is halfway between two numbers, then the nearest whole number away from zero is returned.</returns>
    public static decimal Round(this decimal d, int decimals, MidpointRounding mode)
    {
        if ( mode == MidpointRounding.ToEven )
        {
            return decimal.Round(d, decimals);
        }
        else
        {
            decimal factor = Convert.ToDecimal(Math.Pow(10, decimals));
            int sign = Math.Sign(d);
            return Decimal.Truncate(d * factor + 0.5m * sign) / factor;
        }
    }
}

출처 : http://anderly.com/2009/08/08/silverlight-midpoint-rounding-solution/


-1

맞춤 반올림 사용

public int Round(double value)
{
    double decimalpoints = Math.Abs(value - Math.Floor(value));
    if (decimalpoints > 0.5)
        return (int)Math.Round(value);
    else
        return (int)Math.Floor(value);
}

>.5와 동일한 동작을 생성합니다 Math.Round. 문제는 소수 부분이 정확히 일 때 발생합니다 0.5. Math.Round를 사용하면 원하는 반올림 알고리즘을 지정할 수 있습니다
Panagiotis Kanavos

-2

이것은 모든 지옥처럼 추악하지만 항상 올바른 산술 반올림을 생성합니다.

public double ArithRound(double number,int places){

  string numberFormat = "###.";

  numberFormat = numberFormat.PadRight(numberFormat.Length + places, '#');

  return double.Parse(number.ToString(numberFormat));

}

5
호출 Math.Round하고 반올림하는 방법도 지정합니다.
구성자

-2

내가 해결 해야하는 방법은 다음과 같습니다.

Public Function Round(number As Double, dec As Integer) As Double
    Dim decimalPowerOfTen = Math.Pow(10, dec)
    If CInt(number * decimalPowerOfTen) = Math.Round(number * decimalPowerOfTen, 2) Then
        Return Math.Round(number, 2, MidpointRounding.AwayFromZero)
    Else
        Return CInt(number * decimalPowerOfTen + 0.5) / 100
    End If
End Function

소수점 2 자리로 1.905를 사용하면 예상대로 1.91이되지만 Math.Round(1.905,2,MidpointRounding.AwayFromZero)1.90이됩니다! Math.Round 메소드는 프로그래머가 겪을 수있는 대부분의 기본 문제에 대해 일관되지 않으며 사용할 수 없습니다. 내가 (int) 1.905 * decimalPowerOfTen = Math.Round(number * decimalPowerOfTen, 2)반올림 해야하는 것을 반올림하지 않으려면 원인 을 확인 해야합니다.


Math.Round(1.905,2,MidpointRounding.AwayFromZero)반환1.91
Panagiotis Kanavos
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.