십진수 대신 double을 언제 사용해야합니까?


265

다음 대신 double(또는 float) 을 사용하면 다음과 같은 세 가지 장점을 말할 수 있습니다 decimal.

  1. 적은 메모리를 사용합니다.
  2. 부동 소수점 연산은 프로세서에서 기본적으로 지원하기 때문에 더 빠릅니다.
  3. 더 큰 숫자 범위를 나타낼 수 있습니다.

그러나 이러한 장점은 모델링 소프트웨어에서 볼 수있는 계산 집약적 인 작업에만 적용되는 것으로 보입니다. 물론 재무 계산과 같이 정밀도가 필요한 경우에는 복식을 사용해서는 안됩니다. 그래서 지금 선택할 수있는 실제적인 이유가 double(또는 float대신) decimal"정상"애플리케이션은?

추가 편집 : 모든 훌륭한 답변에 감사드립니다.

또 다른 질문 : 소수의 사람들은 복식이 실수를보다 정확하게 표현할 수 있다고 지적했습니다. 선언했을 때 나는 그들이 더 정확하게 표현할 것이라고 생각합니다. 그러나 부동 소수점 연산을 수행 할 때 정확도가 저하 될 수 있다는 사실이 사실입니까?



5
이것은 꽤 정기적으로 상향 조정되며 여전히 어려움을 겪고 있습니다. 예를 들어, 재무 계산을 수행하는 응용 프로그램을 작성 중이므로 전체에서 십진수를 사용하고 있습니다. 그러나 Math 및 VisualBasic.Financial 함수는 double을 사용하므로 변환을 많이 사용하므로 10 진수 사용을 끊임없이 추측합니다.
Jamie Ide

@JamieIde 금융 함수가 두 배를 사용한다는 것은 미친 짓입니다. 돈은 항상 십진수 여야합니다.
Chris Marisic

@ChrisMarisic 그러나 jamie Ide는 double을 사용하여 레거시 쓰레기를 어떻게 처리 할 수 ​​있습니까? 그런 다음 두 번 더 사용하면 많은 변환으로 반올림 오류가 발생할 수 있습니다 ... 그가 VisualBasic pfffhh를 언급 한 것도 당연합니다 .....
Elisabeth

@ 엘리자베스 10 진수를 올바르게 지원하는 다른 라이브러리를 사용했을 것입니다. VisualBasic.Financial이 제공하는 것은 오늘날 여러 다른 도서관에 존재할 가능성이 있습니다
Chris Marisic

답변:


306

이점을 잘 요약했다고 생각합니다. 그러나 당신은 한 가지 점이 없습니다. 이 decimal유형은 기본 10 개 숫자 (예 : 통화 / 재무 계산에 사용되는 숫자) 를 나타내는 데있어 더 정확 합니다. 일반적으로 double유형은 적어도 큰 정밀도 (잘못되면 누군가를 수정합니다)와 임의의 실수에 대해 확실히 더 빠른 속도를 제공 할 것입니다. 간단한 결론은 : 사용할 것을 고려할 때 제공 double하는 base 10정확성이 필요하지 않은 한 항상 사용 하십시오 decimal.

편집하다:

연산 후 부동 소수점 숫자의 정확도 감소에 대한 추가 질문과 관련하여 약간 더 미묘한 문제입니다. 실제로, 각 작업이 수행 된 후 정밀도 (여기서 정확도를 위해 상호 교환 가능하게 사용하는 용어를 사용함)는 꾸준히 감소합니다. 이것은 두 가지 이유 때문입니다.

  1. 특정 숫자 (가장 명백하게 십진법)를 부동 소수점 형태로 표현할 수 없다는 사실
  2. 손으로 계산하는 것처럼 반올림 오류가 발생합니다. 그러나 이러한 오류가 많은 생각을 할만큼 충분히 중요한지의 여부 (실제로 수행중인 작업 수)에 따라 크게 달라집니다.

모든 경우에 이론적으로 동일하지만 다른 계산을 사용하여 도달 한 두 개의 부동 소수점 숫자를 비교하려면 어느 정도의 허용 오차 (얼마나 많은 차이가 있지만 일반적으로 매우 작은 지)를 허용해야합니다. .

정확도 오류가 발생할 수있는 특정 사례에 대한 자세한 개요는 Wikipedia 기사 의 정확도 섹션을 참조하십시오 . 마지막으로, 기계 수준에서 부동 소수점 숫자 / 연산에 대해 심층적이고 수학적인 토론을 원한다면 자주 인용되는 기사 모든 컴퓨터 과학자가 부동 소수점 산술에 대해 알아야 할 내용을 읽어보십시오 .


1
기수 2로 변환 할 때 정밀도가 손실되는 기수 10의 시험을 제공 할 수 있습니까?
Mark Cidade

@ Mark : 1.000001은 적어도 Jon Skeet에 따르면 하나의 예입니다. (이 페이지의 질문 3 참조 : yoda.arachsys.com/csharp/teasers-answers.html )
Noldorin

25
@Mark : 매우 간단한 예 : 0.1은 밑이 2의주기적인 분수이므로 정확하게로 표현할 수 없습니다 double. 최신 컴퓨터는 여전히 올바른 값을 인쇄하지만 실제로는 정확하게 표현되기 때문이 아니라 결과를 "추측"하기 때문입니다.
Konrad Rudolph

1
Decimal형식은 가수에 약 93 비트의 정밀도를 가지며에 대한 약 52와 비교됩니다 double. 16 바이트까지 채워야하더라도 Microsoft가 IEEE 80 비트 형식을 지원하기를 바랍니다. 초월 연산 (예 : sin (x), log (x) 등)에 대한 지원 double또는 보다 큰 범위 또는 Decimal보다 빠른 속도를 허용했을 것 입니다. DecimalDecimaldouble
supercat

@charlotte : 내 전체 게시물을 읽으면 설명되어 있습니다.
Noldorin

59

부동 소수점 유형을 사용하면 이점을 얻을 수 있습니다. 나는 모든 경우에 소수점을 설계하는 경향이 있으며 프로파일 러를 사용하여 소수점에 대한 작업으로 병목 현상이 발생하거나 속도가 느려지는지 알려줍니다. 이 경우, "다운 캐스트"를 두 번 또는 부동으로 내릴 수 있지만 내부적으로 만 수행하고 수행되는 수학 연산에서 유효 자릿수를 제한하여 정밀 손실을 신중하게 관리하려고합니다.

일반적으로 값이 일시적인 경우 (재사용되지 않는 경우) 부동 소수점 유형을 사용하는 것이 안전합니다. 부동 소수점 유형의 실제 문제점은 다음 세 가지 시나리오입니다.

  1. 부동 소수점 값을 집계하는 중입니다 (이 경우 정밀도 오류가 복합 됨)
  2. 부동 소수점 값을 기반으로 값을 작성합니다 (예 : 재귀 알고리즘)
  3. 당신은 유효 숫자의 매우 넓은 숫자와 수학을하고있다 (예를 들어 123456789.1 * .000000000000000987654321)

편집하다

C # 소수에 대한 참조 문서에 따르면 :

진수 키워드는 128 비트의 데이터 타입을 나타낸다. 부동 소수점 유형과 비교할 때 10 진수 유형은 정밀도와 범위가 작기 때문에 재무 및 금전적 계산에 적합합니다.

위의 진술을 명확히하기 위해 :

나는 모든 경우에 소수점을 설계하는 경향이 있으며 프로파일 러를 사용하여 소수점에 대한 작업으로 병목 현상이 발생하거나 속도가 느려지는지 알려줍니다.

나는 소수가 유리한 산업에서만 일했습니다. 물리학 또는 그래픽 엔진을 사용하는 경우 부동 소수점 유형 (float 또는 double)을 설계하는 것이 훨씬 유리할 것입니다.

십진법은 무한정 정확하지 않지만 (원시 데이터 유형에서 비 적분에 대해 무한 정밀도를 나타내는 것은 불가능합니다), 이중보다 훨씬 더 정확합니다.

  • 십진수 = 28-29 유효 숫자
  • 두자리 수 = 15-16 자리
  • float = 유효 숫자 7

편집 2

에 대한 응답으로 콘라드 루돌프 의 코멘트, 항목 # 1 (위) 확실히 맞습니다. 부정확 한 집계는 실제로 복잡합니다. 예제는 아래 코드를 참조하십시오.

private const float THREE_FIFTHS = 3f / 5f;
private const int ONE_MILLION = 1000000;

public static void Main(string[] args)
{
    Console.WriteLine("Three Fifths: {0}", THREE_FIFTHS.ToString("F10"));
    float asSingle = 0f;
    double asDouble = 0d;
    decimal asDecimal = 0M;

    for (int i = 0; i < ONE_MILLION; i++)
    {
        asSingle += THREE_FIFTHS;
        asDouble += THREE_FIFTHS;
        asDecimal += (decimal) THREE_FIFTHS;
    }
    Console.WriteLine("Six Hundred Thousand: {0:F10}", THREE_FIFTHS * ONE_MILLION);
    Console.WriteLine("Single: {0}", asSingle.ToString("F10"));
    Console.WriteLine("Double: {0}", asDouble.ToString("F10"));
    Console.WriteLine("Decimal: {0}", asDecimal.ToString("F10"));
    Console.ReadLine();
}

이것은 다음을 출력합니다 :

Three Fifths: 0.6000000000
Six Hundred Thousand: 600000.0000000000
Single: 599093.4000000000
Double: 599999.9999886850
Decimal: 600000.0000000000

보시다시피, 동일한 소스 상수에서 추가하더라도 더블의 결과는 정확하지 않지만 (올바르게 반올림하지만) 플로트는 정확도가 떨어집니다. 유효 숫자 2 자리


1
포인트 1이 잘못되었습니다. 정밀도 / 라운딩 오류는 계산이 아닌 캐스팅에서만 발생합니다. 이다 대부분의 수학 연산 따라서 오류를 곱하여, 불안정한 것을 물론 올바른. 그러나 이것은 또 다른 문제이며 제한된 정밀도의 모든 데이터 유형, 특히 10 진수에 동일하게 적용됩니다.
Konrad Rudolph

1
@Konrad Rudolph는 "편집 2"의 예를 1 번 항목에서 만들려고 한 증거로 보았습니다. 종종 부정확 한 결과와 긍정적 인 부정확성이 균형을 이루고 있기 때문에이 문제는 나타나지 않습니다. 집계하지만 동일한 숫자를 집계하면 (예에서 한 것처럼) 문제가 강조 표시됩니다.
Michael Meadows

좋은 예입니다. 저의 주니어 개발자들에게 보여준 것만으로 아이들은 놀랐습니다.
Machado 2012 년

이제 3/5이 아닌 2/3로 동일한 작업을 수행 할 수 있습니다. 2/3를 완벽하게 처리하는 성수 시스템에 대해 알아야합니다.
gnasher729

1
@ gnasher729, 3/5 대신 2/3를 사용 하는 것은 다른 유형에 대해 완벽하게 잘 처리되지 않았습니다 . 흥미롭게도, 부동 소수점 값은 생성 Single: 667660.400000000000되었지만 십진수 값은 생성되었습니다 Decimal: 666666.7000000000. float 값은 올바른 값보다 1000보다 약간 작습니다.
jhenninger 2016 년

25

다른 사람들이 제안한 것처럼 기본 10 값 (예 : 재무 계산)에는 10 진수를 사용하십시오.

그러나 임의 계산 값의 경우 일반적으로 double이 더 정확합니다.

예를 들어, 포트폴리오에서 각 라인의 가중치를 계산하려면 결과가 거의 100 %까지 증가하므로 double을 사용하십시오.

다음 예제에서 doubleResult는 decimalResult보다 1에 가깝습니다.

// Add one third + one third + one third with decimal
decimal decimalValue = 1M / 3M;
decimal decimalResult = decimalValue + decimalValue + decimalValue;
// Add one third + one third + one third with double
double doubleValue = 1D / 3D;
double doubleResult = doubleValue + doubleValue + doubleValue;

포트폴리오의 예를 다시 살펴 보겠습니다.

  • 포트폴리오의 각 라인의 시장 가치는 금전적 가치이며 아마도 10 진수로 표현하는 것이 가장 좋습니다.

  • 포트폴리오의 각 라인의 가중치 (= 시장 가치 / SUM (시장 가치))는 일반적으로 이중으로 표시하는 것이 좋습니다.


6

예를 들어 필자가 작성한 플랫 포머 게임에서 플레이어 속도를 저장하기 위해 부동을 사용했습니다. 분명히 화면에 그림을 그리기 위해 Int로 반올림하기 때문에 여기서는 정밀도가 필요하지 않습니다.


3
정밀도는 소수의 유일한 이점이므로 이것이 맞습니다. 소수점 이하에 부동 소수점 숫자를 사용해야하는지 묻지 않아야합니다. 그것은 당신의 첫 생각이어야합니다. 그렇다면 문제는 소수점을 사용해야 할 때입니다 (정확도가 중요한 경우에는 정답이 여기 있습니다).
인스턴스 헌터

3
@Daniel Straight, 재미 있지만 반대 의견이 있습니다. 나는 성능 특성 때문에 덜 정확한 유형을 사용하는 것이 사전 최적화에 달려 있다고 생각합니다. 그 이점을 실현하기 전에 사전 최적화 비용을 여러 번 지불해야 할 수도 있습니다.
Michael Meadows

3
@Michael Meadows, 나는이 주장을 이해할 수 있습니다. 그러나 조기 최적화에 대한 주요 불만 중 하나는 프로그래머가 느리게 진행되는 것을 알지 못한다는 것입니다. 그러나 우리는 소수가 두 배보다 느리다는 것을 의심의 여지없이 알고 있습니다. 그럼에도 불구하고 대부분의 경우 성능 향상이 사용자에게 눈에 띄지 않을 것이라고 생각합니다. 물론 대부분의 경우 정밀도도 필요하지 않습니다. 허.
인스턴스 헌터

십진 부동 소수점은 실제로 동일한 수의 비트를 사용하는 이진 부동 소수점보다 정확하지 않습니다. Decimal의 장점은 재무 계산에서 일반적으로 사용되는 0.01과 같은 DECIMAL 분수를 정확하게 나타낼 수 있다는 것입니다.
dan04

글쎄, 이것은 정확하지 않습니다 :)-많은 게임에서 부동 소수점 숫자는 일관성이 없기 때문에 바람직하지 않을 수 있습니다. 참조 여기
BlueRaja - 대니 Pflughoeft

4

일부 회계에서는 정수 유형을 대신 사용하거나 함께 사용할 가능성을 고려하십시오. 예를 들어, 귀하가 운영하는 규칙은 모든 계산 결과에 소수점 이하 6 자리 이상 이월해야하며 최종 결과는 가장 가까운 페니로 반올림됩니다.

$ 100의 1/6을 계산하면 $ 16.66666666666666 ...이 산출되므로 워크 시트에서 수행되는 값은 $ 16.666667이됩니다. double과 decimal은 결과를 소수점 6 자리까지 정확하게 산출해야합니다. 그러나 결과를 정수 16666667으로 전달하여 누적 오류를 피할 수 있습니다. 이후의 각 계산은 동일한 정밀도로 수행하고 유사하게 수행 할 수 있습니다. 예를 계속해서, 그 금액에 대한 텍사스 판매 세를 계산합니다 (16666667 * .0825 = 1375000). 두 개를 추가하면 (짧은 워크시 트임) 1666667 + 1375000 = 18041667. 소수점을 다시 이동하면 18.041667 또는 $ 18.04가됩니다.

이 짧은 예제는 double 또는 decimal을 사용하여 누적 오류를 생성하지는 않지만 단순히 double 또는 decimal을 계산하고 이월하면 심각한 오류가 누적되는 경우를 쉽게 보여줄 수 있습니다. 작동하는 규칙에 소수의 소수 자릿수가 필요한 경우 10 ^ (소수 자릿수 #)를 곱한 후 각 값을 정수로 저장 한 다음 10 ^ (소수 자릿수 #)로 나누어 실제 값을 구하십시오. 값은 누적 오류를 방지합니다.

동전의 일부가 발생하지 않는 상황 (예 : 자동 판매기)에서는 비 통합 유형을 전혀 사용할 이유가 없습니다. 단순히 달러가 아닌 동전을 세는 것으로 생각하십시오. 모든 계산에 전체 동전 만 포함되는 코드를 보았지만 이중을 사용하면 오류가 발생했습니다! 정수만 수학으로 문제를 제거했습니다. 따라서 가능하지 않은 두 번째와 십진수를 모두 잊어 버렸습니다.


3

다른 언어 또는 플랫폼과 이진 삽입해야하는 경우 표준화 된 float 또는 double을 사용해야합니다.


2

참고 :이 게시물은 http://csharpindepth.com/Articles/General/Decimal.aspx 의 10 진수 유형 기능 정보 와 그 의미에 대한 내 자신의 해석을 기반으로합니다. Double이 일반적인 IEEE 배정도라고 가정하겠습니다.

참고 2 :이 게시물에서 가장 작고 가장 큰 숫자의 수를 나타냅니다.

"소수"의 장점.

  • "소수"는 (충분히 짧은) 소수로 표기 할 수있는 숫자를 정확하게 표현할 수 있으며 이중은 불가능합니다. 이것은 재무 장부에서 중요하며 결과가 계산을 수행하는 인간의 결과와 정확히 일치하는 것이 중요한 경우 유사합니다.
  • "소수"는 "이중"보다 훨씬 큰 가수를가집니다. 즉, 정규화 된 범위 내의 "소수"값은 double보다 훨씬 높은 정밀도를 갖습니다.

소수의 단점

  • 훨씬 느려질 것입니다 (벤치 마크는 없지만 적어도 10 배 이상 더 추측 할 것입니다). 십진수는 하드웨어 가속의 이점을 얻지 못하고 산술은 10의 거듭 제곱으로 상대적으로 비싼 곱셈 / 나눗셈이 필요합니다 ( 이는 곱셈과 나눗셈에 비해 2) 제곱에 비해 훨씬 비쌉니다.
  • 십진법은 십진법보다 먼저 오버 플로우됩니다. 10 진수는 최대 ± 2 96-1 까지의 숫자 만 나타낼 수 있습니다 . 비교에 의해 double은 최대 ± 2 1024 까지의 숫자를 나타낼 수 있습니다
  • 십진수가 먼저 흐릅니다. 10 진수로 표시 할 수있는 가장 작은 숫자는 ± 10-28 입니다. 비교에 의해 double은 subnromal 숫자가 지원되는 경우 2 -149 (약 10 -45 ) 까지의 값을 나타내고, 그렇지 않은 경우 2 -126 (약 10 -38 )입니다.
  • 십진수는 두 배의 메모리를 두 배로 차지합니다.

내 의견은 돈 계산과 인간 계산과 정확히 일치하는 다른 경우에는 "소수"를 기본값으로 사용하고 나머지 시간에는 기본 선택으로 double을 사용해야한다는 의견입니다.


2

필요한 것에 따라 다릅니다.

float 및 double은 이진 데이터 형식 이므로 반올림 수의 방식으로 몇 가지 어려움과 errrors가 있으므로 double은 0.1에서 0.100000001490116로 반올림하고 double은 1/3에서 0.33333334326441로 반올림합니다. 간단히 말해서 모든 실수가 이중 유형으로 정확하게 표현되는 것은 아닙니다

운 좋게도 C #은 소위 십진 부동 소수점 산술도 지원합니다. 여기서 숫자는 이진 시스템이 아닌 십진수 시스템을 통해 표시됩니다. 따라서 10 진수 부동 소수점 산술 부동 소수점 숫자를 저장하고 처리 할 때 정확성을 잃지 않습니다 . 따라서 높은 수준의 정확도가 필요한 계산에 매우 적합합니다.


0

정확성보다 성능을 높이 평가하려면 부동 소수점을 사용하십시오.


6
10 진수는 때때로 (항상 항상 그런 것은 아님) 제한된 특정 경우를 제외하고는 더 정확하지 않습니다.
David Thornley

0

응용 프로그램의 기능 유형을 선택하십시오. 재무 분석과 같이 정밀도가 필요한 경우 질문에 대한 답변이 있습니다. 그러나 응용 프로그램이 예상대로 두 배로 해결할 수 있다면.

응용 프로그램에서 빠른 계산이 필요합니까? 아니면 전 세계에서 항상 답변을 드릴까요? 실제로 응용 프로그램 유형에 따라 다릅니다.

배고픈 그래픽? float 또는 double로 충분합니다. 재무 데이터 분석, 유성 행성 종류의 정밀 타격? 그것들은 약간의 정밀도가 필요합니다 :)


8
10 진수도 추정치입니다. 그것들은 재무 산술 규칙을 준수하지만 물리와 관련된 계산에는 이점이 없습니다.
David Thornley

0

십진수는 더 넓은 바이트를 가지며 이중으로 CPU에서 기본적으로 지원됩니다. 10 진수는 10 진수이므로 10 진수를 계산하는 동안 10 진수를 2 배로 변환합니다.

For accounting - decimal
For finance - double
For heavy computation - double

.NET CLR은 Math.Pow (double, double) 만 지원합니다. 십진수는 지원되지 않습니다.

.NET Framework 4

[SecuritySafeCritical]
public static extern double Pow(double x, double y);

0

표기법이 10 진수 표시보다 짧은 경우 기본적으로 이중 값이 과학적 표기법으로 직렬화됩니다. (예 : .00000003은 3e-8입니다.) 10 진수 값은 과학적 표기법으로 연속되지 않습니다. 외부 당사자의 소비를 위해 직렬화 할 때 고려해야 할 사항입니다.

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