2014-07-05 4 views
6

"높은"정밀도 Double을 Decimal로 변환 할 때 Convert.ToDecimal로 정밀도가 떨어지거나 반올림으로 (10 진수)로 캐스팅됩니다.십진수에서 두 자리로 반올림하지 않고 15 자리에서 반올림

(더블) Convert.ToDecimal 의해 반환

double d = -0.99999999999999956d; 
decimal result = Convert.ToDecimal(d); // Result = -1 
decimal result = (Decimal)(d); // Result = -1 

진수 값은 15 개 개의 유효 숫자의 최대를 포함한다. value 매개 변수에 15 자리 이상의 유효 자릿수가 포함 된 경우 가장 가까운 값으로 반올림하여 반올림됩니다.

그래서 난 내 정밀도를 유지하기 위해, 나는 Convert.ToDecimal (String)를 문자열로 내 이중 변환 한 후 호출 할 필요가 :이 방법은 작업 있지만

decimal result = System.Convert.ToDecimal(d.ToString("G20")); // Result = -0.99999999999999956d 

내가 사용하지 않도록하고 싶습니다 15 자리수 후에 반올림하지 않고 Double을 Decimal로 변환하기위한 String 변수?

+0

'd'의 범위를 미리 알고 있습니까? 난 의사 코드에서 가벼운 솔루션을 제공 할 수 있습니다. (C#으로 쓰려면 익숙하지 않습니다.)'d'가 [-2..2]와 같은 범위에 있다는 것을 알고 있다면 –

+0

d는 항상 [- 1,1] –

+0

나는 이걸 다른 곳에서 온 'double'이라고 생각하고있어. 너는 바꿀 수 없어? get-go에서 십진수로 선언 된 경우 ('decimal d = -.99.999999999999956m;') 해당 정밀도가 유지됩니다. –

답변

4

가능한 해결책 중 하나는 N 개의 double 값의 정확한 합계로 분해하는 것입니다. 마지막은 작고 십진수로 변환 할 때 원하는 모든 후행 유효 숫자와 첫 번째 (n-1) 십진수로 정확하게 변환하십시오. 작업 d -=은 C#을 더 높은 정밀도 이상에서 이진 부동 소수점 연산을 계산하는 경우에도, 정확한 것을

decimal t = 0M; 
    bool b = d < 0; 
    if (b) d = -d; 
    if (d >= 0.5) { d -= 0.5; t = 0.5M; } 
    if (d >= 0.25) { d -= 0.25; t += 0.25M; } 
    if (d >= 0.125) { d -= 0.125; t += 0.125M; } 
    if (d >= 0.0625) { d -= 0.0625; t += 0.0625M; } 
    t += Convert.ToDecimal(d); 
    if (b) t = -t; 

Test it on ideone.com.

참고 : d -1.0 사이 1.0을 두 번 소스에 대한

double (자체적으로 허용)

이것은 double에서 string으로의 변환보다 저렴하며 결과에 몇 가지 추가 자릿수의 정확도를 제공합니다 (위 네 개의 if-then-elses에 대해 4 비트의 정확도).

비고 : C 번호 자체가 더 높은 정밀도 부동 소수점 연산을 할 수 있도록하지 않으면, 좋은 트릭은 십진수로 정확히 각을 변환하는 것 두 값 d1d2으로 d을 분할 데커 분할을 사용했을 것이다. 아아, Dekker 분할은 IEEE 754 곱셈과 덧셈의 엄격한 해석으로 만 작동합니다.


다른 아이디어는 유효수 s을 얻었다 frexp의 C#의 버전을 사용 de를 지수 및 (Decimal)((long) (s * 4503599627370496.0d)) * <however one computes 2^e in Decimal>를 계산하는 것이다.

+0

코드 샘플을 시도했지만 -1이 표시됩니다. 당신의 편에서 그것을 재현 할 수 있습니까? –

+0

@StevenMuhr 죄송합니다. C# 또는 .NET 코드를 실행할 수있는 모든 플랫폼이 없지만 ideone.com으로 게임을하고 다시 작동 해 보겠습니다. –

+0

@StevenMuhr Method1, 결과 : 0.9999999999999996입니다. 'd '기호에 대한 처리를 추가해야합니다. http://ideone.com/MevINX –

1

두 가지 방법 중 하나가 2^63 이하의 값에 대해 작동하고 다른 하나는 2^53보다 큰 값에 대해 작동합니다.

작은 값을 정수 및 소수 부분으로 나눕니다. 전체 숫자 부분은 long으로 정확하게 캐스팅 될 수 있으며 Decimal으로 캐스트 될 수 있습니다. [Decimal에 대한 직접 캐스트가 정확하지 않을 수 있습니다.] 소수 부분에 정확히 9007199254740992.0 (2^53)을 곱하고 long으로 변환 한 다음 Decimal으로 변환 할 수 있습니다. 그 다음 9007199254740992.0m으로 나눕니다. 해당 부분의 결과를 정수부에 더하면 Decimal 값을 가져야합니다.이 값은 정확한 최소 유효 자릿수 중 하나입니다 [정확하게 반올림되지는 않지만 내장 된 변환보다 훨씬 더 좋습니다!]

큰 값의 경우, (1.0/281474976710656.0) (2^-48)을 곱하고 그 결과의 정수 부분을 취해서 다시 281474976710656.0을 곱한 다음 원래 결과에서 빼십시오. 나눗셈과 빼기의 전체 결과를 Decimal (정확하게 변환해야 함)으로 변환하고 전을 281474976710656m으로 곱하고 후자를 더합니다.

관련 문제