문제

관련된 빠른 실험을 실행합니다 .NET에서 이중 곱셈이 끊어 졌습니까? 그리고 C# 문자열 서식에 관한 몇 가지 기사를 읽으면서 다음과 같이 생각했습니다.

{
    double i = 10 * 0.69;
    Console.WriteLine(i);
    Console.WriteLine(String.Format("  {0:F20}", i));
    Console.WriteLine(String.Format("+ {0:F20}", 6.9 - i));
    Console.WriteLine(String.Format("= {0:F20}", 6.9));
}

이 C 코드와 동일합니다.

{
    double i = 10 * 0.69;

    printf ( "%f\n", i );
    printf ( "  %.20f\n", i );
    printf ( "+ %.20f\n", 6.9 - i );
    printf ( "= %.20f\n", 6.9 );
}

그러나 C#은 출력을 생성합니다.

6.9
  6.90000000000000000000
+ 0.00000000000000088818
= 6.90000000000000000000

나는 디버거의 값 6.8999999999999999946709 (6.9가 아닌)와 동일하게 나타납니다.

C와 비교하여 형식으로 요청 된 정밀도를 보여줍니다.

6.900000                          
  6.89999999999999946709          
+ 0.00000000000000088818          
= 6.90000000000000035527          

무슨 일이야?

(Microsoft .NET Framework 버전 3.51 SP1 / Visual Studio C# 2008 Express Edition)


수치 컴퓨팅 및 간격 산술 구현 경험 - 다양한 플랫폼에서 복잡한 수치 시스템의 정밀도 제한으로 인한 오류를 추정하는 기술입니다. 현상금을 얻으려면 저장 정밀도에 대해 설명하고 설명하지 마십시오.이 경우 64 비트의 ULP의 한 ULP의 차이입니다.

현상금을 얻으려면 .NET이 C 코드에 표시된대로 요청 된 정밀도의 두 배를 어떻게 (또는)하는 방법을 알고 싶습니다.

도움이 되었습니까?

해결책

문제는 .NET이 항상 a를 둥글게한다는 것입니다 double 15 개의 중요한 소수점 숫자 ~ 전에 형식에 의해 요청 된 정밀도와 이진수의 정확한 소수점 값에 관계없이 서식 적용.

Visual Studio Debugger에는 내부 이진 번호에 직접 액세스하는 자체 형식/디스플레이 루틴이있어 C# 코드, C 코드 및 디버거 간의 불일치가 있습니다.

정확한 소수점 값에 액세스 할 수있는 내장은 없습니다. double, 또는 포맷 할 수 있도록 a double 소수점 이하의 특정 수치로, 그러나 내부 바이너리 숫자를 골라서 소수점 값의 문자열 표현으로 재건함으로써 직접 할 수 있습니다.

또는 Jon Skeet을 사용할 수 있습니다 DoubleConverter 수업 (그의 것에서 연결되어 있습니다 "바이너리 플로팅 포인트 및 .NET"기사). 이것은 a ToExactString 정확한 소수점 값을 반환하는 메소드 double. 출력을 특정 정밀도로 반올림 할 수 있도록이를 쉽게 수정할 수 있습니다.

double i = 10 * 0.69;
Console.WriteLine(DoubleConverter.ToExactString(i));
Console.WriteLine(DoubleConverter.ToExactString(6.9 - i));
Console.WriteLine(DoubleConverter.ToExactString(6.9));

// 6.89999999999999946709294817992486059665679931640625
// 0.00000000000000088817841970012523233890533447265625
// 6.9000000000000003552713678800500929355621337890625

다른 팁

Digits after decimal point
// just two decimal places
String.Format("{0:0.00}", 123.4567);      // "123.46"
String.Format("{0:0.00}", 123.4);         // "123.40"
String.Format("{0:0.00}", 123.0);         // "123.00"

// max. two decimal places
String.Format("{0:0.##}", 123.4567);      // "123.46"
String.Format("{0:0.##}", 123.4);         // "123.4"
String.Format("{0:0.##}", 123.0);         // "123"
// at least two digits before decimal point
String.Format("{0:00.0}", 123.4567);      // "123.5"
String.Format("{0:00.0}", 23.4567);       // "23.5"
String.Format("{0:00.0}", 3.4567);        // "03.5"
String.Format("{0:00.0}", -3.4567);       // "-03.5"

Thousands separator
String.Format("{0:0,0.0}", 12345.67);     // "12,345.7"
String.Format("{0:0,0}", 12345.67);       // "12,346"

Zero
Following code shows how can be formatted a zero (of double type).

String.Format("{0:0.0}", 0.0);            // "0.0"
String.Format("{0:0.#}", 0.0);            // "0"
String.Format("{0:#.0}", 0.0);            // ".0"
String.Format("{0:#.#}", 0.0);            // ""

Align numbers with spaces
String.Format("{0,10:0.0}", 123.4567);    // "     123.5"
String.Format("{0,-10:0.0}", 123.4567);   // "123.5     "
String.Format("{0,10:0.0}", -123.4567);   // "    -123.5"
String.Format("{0,-10:0.0}", -123.4567);  // "-123.5    "

Custom formatting for negative numbers and zero
String.Format("{0:0.00;minus 0.00;zero}", 123.4567);   // "123.46"
String.Format("{0:0.00;minus 0.00;zero}", -123.4567);  // "minus 123.46"
String.Format("{0:0.00;minus 0.00;zero}", 0.0);        // "zero"

Some funny examples
String.Format("{0:my number is 0.0}", 12.3);   // "my number is 12.3"
String.Format("{0:0aaa.bbb0}", 12.3);

이것을 살펴보십시오 MSDN 참조. 메모에서 숫자는 요청 된 소수점 자리 수로 반올림되어 있다고 명시하고 있습니다.

대신 "{0 : r}"를 사용하면 "왕복"값이라고하는 것을 생성합니다. MSDN 참조 자세한 내용은 여기 내 코드와 출력이 있습니다.

double d = 10 * 0.69;
Console.WriteLine("  {0:R}", d);
Console.WriteLine("+ {0:F20}", 6.9 - d);
Console.WriteLine("= {0:F20}", 6.9);

산출

  6.8999999999999995
+ 0.00000000000000088818
= 6.90000000000000000000

이 질문은 결국 폐쇄되었지만, 나는이 혐오감이 어떻게 존재했는지 언급 할 가치가 있다고 생각합니다. 어떤면에서, 당신은 c# 사양을 비난 할 수 있으며, 이는 더블의 정밀도가 15 ~ 16 자리 (IEEE-754의 결과)를 가져야한다고 명시합니다. 조금 더 (섹션 4.1.6) 구현이 사용할 수 있다고 언급되어 있습니다. 더 높은 정도. 당신을 염두에 두십시오 : 더 높은, 더 낮지 않습니다. 그들은 심지어 IEEE-754에서 벗어날 수 있습니다 : 유형의 표현 x * y / z 어디 x * y 양보 할 것입니다 +/-INF 그러나 나눈 후 유효한 범위에 있으면 오류가 발생할 필요가 없습니다. 이 기능을 사용하면 컴파일러가 더 나은 성능을 제공 할 수있는 아키텍처에서 더 높은 정밀도를 사용할 수 있습니다.

그러나 나는 "이유"를 약속했다. 다음은 인용문입니다 (최근 의견 중 하나에 리소스를 요청했습니다). 공유 소스 CLI, 안에 clr/src/vm/comnumber.cpp:

"전시 및 왕복이 가능한 숫자를 제공하기 위해 15 자리를 사용하여 숫자를 구문 분석 한 다음 동일한 값으로 이동하는지 확인합니다. 그렇다면 해당 숫자를 문자열로 변환합니다. 17 자리 숫자를 사용하여 다시 표시하고 표시합니다. "

다시 말해서, MS의 CLI 개발 팀은 왕복하기 쉬우 며 읽기 어려운 고통이 아닌 예쁜 가치를 보여 주기로 결정했습니다. 좋거나 나쁘거나? 옵트 인 또는 옵트 아웃을 원합니다.

주어진 숫자 의이 왕복 성을 찾는 속임수? 일반 수 구조 (이중의 특성에 대한 별도의 필드가있는)와 뒤로 변환 한 다음 결과가 다른지 비교하십시오. 다른 경우 정확한 값이 사용됩니다 (중간 값에서와 같이 6.9 - i) 동일하면 "예쁜 가치"가 사용됩니다.

이미 Andyp에 대한 의견으로 언급했듯이 6.90...00 조금 동일합니다 6.89...9467. 그리고 이제 당신은 이유를 알고 있습니다 0.0...8818 사용됩니다 : 그것은 약간 다릅니다 0.0.

이것 15 자리 배리어 하드 코딩되며 CLI를 다시 컴파일하거나 Mono를 사용하여 Microsoft를 호출하여 전체 "정밀도"를 인쇄 할 수있는 옵션을 추가하도록 설득하여 변경할 수 있습니다 (실제로 정밀도가 아니라 더 나은 단어가 부족하여). . 52 비트 정밀도를 직접 계산하거나 앞에서 언급 한 라이브러리를 사용하는 것이 더 쉽습니다.

편집 : IEE-754 플로팅 포인트로 직접 실험하고 싶다면 이 온라인 도구를 고려하십시오, 부동 소수점의 모든 관련 부분을 보여줍니다.

사용

Console.WriteLine(String.Format("  {0:G17}", i));

그것은 당신에게 17 자리를 모두 줄 것입니다. 기본적으로 이중 값에는 최대 17 자리가 내부적으로 유지되지만 15 자리 숫자는 15 자리 숫자를 포함합니다. {0 : r}은 항상 17 자리 숫자를주지는 않습니다. 그 숫자를 그 정밀도로 표현할 수 있다면 15가 줄어 듭니다.

숫자를 해당 정밀도로 표현할 수있는 경우 15 자리 또는 숫자를 최대 정밀도로 만 표시 할 수있는 경우 17 자리를 반환합니다. 더블 리턴을 더 많은 숫자를 구현하는 방식 인 더 많은 숫자를 만들기 위해 할 수있는 일은 없습니다. 마음에 들지 않으면 새로운 이중 수업을 직접 수행합니다 ...

.NET의 Double Cant는 17 자리 이상을 저장하므로 디버거에는 6.89999999999999995가 표시됩니다. 우리를 잘못 증명할 수있는 이미지를 제공하십시오.

이것에 대한 대답은 간단하고 찾을 수 있습니다. MSDN

부동 소수점 번호는 소수점 숫자 만 근사 할 수 있으며 부동 소수점 번호의 정밀도는 그 숫자가 소수점 수치를 얼마나 정확하게하는지 결정합니다. 기본적으로 이중 값에는 포함됩니다 15 소수점 숫자 정밀도, 최대 17 자리는 내부적으로 유지되지만.

귀하의 예에서 I의 값은 6.899999999999999946709로 3 자와 16 자리 사이의 모든 위치에 대한 숫자 9를 갖습니다 (숫자의 정수 부분을 계산해야합니다). 문자열로 변환 할 때 프레임 워크는 숫자를 15 자리로 반올림합니다.

i     = 6.89999999999999 946709
digit =           111111 111122
        1 23456789012345 678901

나는 당신의 결과를 재현하려고 노력했지만 디버거에서 'i'를 보았을 때 질문에 쓴 것처럼 '6.8999999999999999995'가 아닌 '6.899999999999999995'로 나타났습니다. 본 것을 재현하기위한 단계를 제공 할 수 있습니까?

디버거가 표시하는 내용을 보려면 다음 코드 줄에서와 같이 DoubleConverter를 사용할 수 있습니다.

Console.WriteLine(TypeDescriptor.GetConverter(i).ConvertTo(i, typeof(string)));

도움이 되었기를 바랍니다!

편집 : 나는 내가 생각했던 것보다 더 피곤하다고 생각합니다. 물론 이것은 왕복 값 (앞에서 언급 한 바와 같이)에 대한 형식과 동일합니다.

대답은 그렇습니다. 이중 인쇄는 .NET에서 이중 인쇄가 고장 났고 후행 쓰레기 숫자를 인쇄하고 있습니다.

올바르게 구현하는 방법을 읽을 수 있습니다 여기.

나는 ironscheme을 위해 똑같이해야했다.

> (* 10.0 0.69)
6.8999999999999995
> 6.89999999999999946709
6.8999999999999995
> (- 6.9 (* 10.0 0.69))
8.881784197001252e-16
> 6.9
6.9
> (- 6.9 8.881784197001252e-16)
6.8999999999999995

참고 : C와 C#은 모두 정확한 값을 가지고 있으며 인쇄가 깨진 것입니다.

업데이트 : 나는 여전히이 발견으로 이어진 메일 링리스트 대화를 찾고 있습니다.

이 빠른 수정을 찾았습니다.

    double i = 10 * 0.69;
    System.Diagnostics.Debug.WriteLine(i);


    String s = String.Format("{0:F20}", i).Substring(0,20);
    System.Diagnostics.Debug.WriteLine(s + " " +s.Length );

console.writeline (String.format ( "코스 요금은 {0 : 0.00}", +cfees);

internal void DisplaycourseDetails()
        {
            Console.WriteLine("Course Code : " + cid);
            Console.WriteLine("Couse Name : " + cname);
            //Console.WriteLine("Couse Name : " + string.Format("{0:0.00}"+ cfees));
            // string s = string.Format("Course Fees is {0:0.00}", +cfees);
            // Console.WriteLine(s);
            Console.WriteLine( string.Format("Course Fees is {0:0.00}", +cfees));
        }
        static void Main(string[] args)
        {
            Course obj1 = new Course(101, "C# .net", 1100.00);
            obj1.DisplaycourseDetails();
            Course obj2 = new Course(102, "Angular", 7000.00);
            obj2.DisplaycourseDetails();
            Course obj3 = new Course(103, "MVC", 1100.00);
            obj3.DisplaycourseDetails();
            Console.ReadLine();
        }
    }
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top