[C#] double형 오차, Round Off 대체 방법 및 decimal 리터럴 사용 방법

Double의 반올림 에러(Round-off) 대체 방법

  우선 이 방법은 제목에도 적혀있듯, 해결 방법이 아닌 대체 방법을 서술합니다.

  왜 대체 방법일수밖에 없느냐면, 구조적으로 double형은 반올림 오류가 발생할 수밖에 없기 때문입니다. 아래를 보시죠.


var a = 2.42;
var b = a + 0.01;
Console.WriteLine(b);


  위를 실행하면 아마 콘솔에 정상적으로 2.43이 출력될 겁니다. 하지만 정말 그럴까요? 디버그를 통해 b 변수의 값을 조사해보면, 실제로는 2.4299999999999997라는 값이 저장된 것을 확인할 수 있습니다. 한 번 확인해봅시다.

var a = 2.42;
for (var i = 0; i < 100; i++)
    a += 0.01;
Console.WriteLine(a);


  콘솔에 어떤 값이 출력되시나요? 하나 확실한 것은, 3.42는 아닐 겁니다. 출력된 값은 3.41999999999998이죠. 이유는 바로 반올림 에러입니다.

  위 링크와 double, float이 구현되는 방식을 보시면 왜 이런 문제가 생기는지 알 수 있을 겁니다. 하지만 C#에서는 이러한 문제를 해결하기 위해 decimal형을 지원합니다. decimal형은 MSDN 문서에서 보듯, round off 오류가 일어나지 않기 때문에 정밀도가 요구되는 작업에서 사용하면 좋습니다.

  문제는, decimal형은 그 크기가 16바이트이며, 연산 속도도 double에 비해 느립니다. 테스트 결과, 0부터 1,000,000까지 0.1씩 더하는, 총합 천만 회의 루프에서 double은 0.03초, decimal은 0.23초로 decimal이 7배 이상 느린 것으로 확인되었습니다.

  따라서, double의 기본 정밀도 보장 범위 내의 변동에서 문제가 없는 프로그램이라면 double형을, 성능과 자원을 더 할당해서라도 정밀도가 보장돼야 하는 프로그램에서는 decimal을 사용하시면 되겠습니다.

Decimal 리터럴

  여러 언어에서 공통으로 사용되는 리터럴이 있습니다. #.0f는 float, #.0은 double, #l은 long, 0은 int, ...

  마찬가지로, Decimal형식도 C#에서 리터럴이 존재합니다. 바로 m입니다. 0.01m은 Decimal형의 0.01을 의미합니다.

F:  float
D:  double
U:  uint
L:  long
UL: ulong
M:  decimal

댓글

이 블로그의 인기 게시물

C# 남아도는 메모리에도 불구하고 OutOfMemoryException이 발생한다면?

MySQL 데이터 타입과 Java 데이터 타입 비교/매칭

테일즈위버 OST 전곡 모음