it-source

부동 소수점 번호의 정확한 값은 어떻게 인쇄합니까?

criticalcode 2023. 7. 25. 21:05
반응형

부동 소수점 번호의 정확한 값은 어떻게 인쇄합니까?

우선, 이것은 부동소수점 초보 질문이 아닙니다.저는 부동소수점 산술(초월함수는 말할 것도 없고)의 결과는 대개 정확하게 표현될 수 없으며, 대부분의 종단 소수는 이진 부동소수점 숫자로 정확하게 표현될 수 없다는 것을 알고 있습니다.

각 즉, 각한부소합와값수점정은동이유확게다항리일하수치니합수리가능합와▁(▁that▁value▁point▁cor,▁eacha다,일)에 정확히 합니다.p/qq는 2)의 거듭제곱이며, 이는 정확한 십진수 표현을 갖습니다.

다음과 같습니다: 표현을 효율적으로요?이 정확한 소수 표현을 효율적으로 찾는 방법은 무엇입니까?sprintf및 유사한 함수는 일반적으로 원래 부동 소수점 값을 고유하게 결정하기 위해 최대 수의 유의한 자릿수까지만 지정되며, 정확한 소수점 표현을 인쇄할 필요는 없습니다. 중 , 제가사용알알을있, 매느립다니.O(e^2)e는 다음과 같습니다개요는 다음과 같습니다.

  1. 맨티사를 10진수 정수로 변환합니다.이렇게 하려면 비트를 분리하여 맨티사를 직접 읽거나, 먼저 값을 2의 거듭제곱하여 1<=x<10 범위에 넣은 다음 int로 캐스팅하고 감산한 다음 10을 곱하여 한 번에 한 자리를 잘라내는 지저분한 부동 소수점 루프를 작성할 수 있습니다.
  2. 반복적으로 곱하거나 2로 나누어 지수를 적용합니다.이것은 사용자가 생성한 10진수 문자열에 대한 작업입니다.~3회 곱할 때마다 왼쪽에 숫자가 하나 더 추가됩니다.분할할 때마다 오른쪽에 숫자가 추가됩니다.

이게 정말 최선일까요?글쎄요.하지만 저는 부동소수점 전문가가 아니며 부정확한 결과가 나오지 않는 한 숫자의 부동소수점 표현에 대한 기본 10 계산을 수행할 수 있는 방법을 찾을 수 없습니다(2의 거듭제곱 또는 거듭제곱은 빈 비트가 있다는 것을 알지 못하는 한 부동소수점 숫자에 대한 손실 연산입니다).

이 질문은 관료적인 부분과 알고리즘적인 부분이 있습니다.부동 소수점 번호는 내부적으로 (2e × m)로 저장됩니다. 여기서 e는 지수이고 m은 가수입니다.문제의 관료적인 부분은 이 데이터에 액세스하는 방법이지만, R.은 문제의 알고리즘 부분, 즉 (2e × m)을 소수점 형태의 분수 (a/b)로 변환하는 것에 더 관심이 있는 것 같습니다.여러 언어로 된 관료적인 질문에 대한 대답은frexp(이것은 오늘 전에는 몰랐던 흥미로운 세부 사항입니다.)

언뜻 보기에 십진수로 2를 쓰는e 데는 O(e2) 작업이 필요하고, 아직은 가수에게 더 많은 시간이 필요한 것이 사실입니다.그러나 Schönhage-Strassen 고속 곱셈 알고리즘의 마법 덕분에 Δ(e) 시간 내에 할 수 있습니다. 여기서 타일은 "로그 인자까지"를 의미합니다.만약 여러분이 쇤하게-스트라센을 마술로 본다면, 무엇을 해야 할지 생각하는 것은 그리 어렵지 않습니다.e가 짝수이면 2를 재귀적으로 계산한e/2 다음 빠른 곱셈을 사용하여 제곱할 수 있습니다.반면에 e가 홀수이면 2를 재귀적으로 계산한e−1 다음 두 배로 계산할 수 있습니다.10번 베이스에 Schönhage-Strassen 버전이 있는지 확인하기 위해 주의해야 합니다.널리 문서화되지는 않았지만, 어떤 기반에서든 수행할 수 있습니다.

매우 긴 가수를 이진에서 기본 10으로 변환하는 것은 정확히 같은 질문은 아니지만 비슷한 대답을 가지고 있습니다.맨티사를 두 의 반으로k 나눌 있습니다. m = a × 2 + b.그런 다음 a와 b를 재귀적으로 기본 10으로 변환하고, 2를 기본 10으로 변환한k 다음 다른 빠른 곱셈을 수행하여 기본 10에서 m을 계산합니다.

이 모든 것 뒤에 있는 추상적인 결과는 Δ(N) 시간에 정수를 한 기저에서 다른 기저로 변환할 수 있다는 것입니다.

만약 질문이 표준 64비트 부동소수점 숫자에 관한 것이라면, 그것은 화려한 쇤하게-스트라센 알고리즘에 비해 너무 작습니다.이 범위에서는 다양한 트릭으로 작업을 저장할 수 있습니다.한 가지 접근 방식은 2의e 모든 2048 값을 룩업 테이블에 저장한 다음 비대칭 곱셈(긴 곱셈과 짧은 곱셈 사이)을 사용하여 작업하는 것입니다.또 다른 방법은 기본 10이 아닌 기본 10000(또는 아키텍처에 따라 10의 더 높은 거듭제곱)에서 작업하는 것입니다.그러나 R.가 논평에서 지적했듯이, 128비트 부동 소수점 숫자는 이미 룩업 테이블과 표준 긴 곱셈 모두에 의문을 제기할 수 있을 정도로 충분히 큰 지수를 허용합니다.실질적으로 긴 곱셈은 소수의 자릿수까지 가장 빠르며, 상당한 중간 범위에서 카라츠바 곱셈 또는 툼-쿡 곱셈을 사용할 수 있으며, 그 이후에는 이론뿐만 아니라 실제에서도 쇤하게-스트라센의 변형이 가장 좋습니다.

실제로, 큰 정수 패키지 GMP는 이미 Δ(N) 시간 기수 변환뿐만 아니라 어떤 곱셈 알고리즘을 선택할 수 있는지에 대한 우수한 휴리스틱도 가지고 있습니다.그들의 솔루션과 저의 솔루션의 유일한 차이점은 기본 10에서 큰 산술을 하는 대신 기본 2에서 10의 큰 거듭제곱을 계산한다는 것입니다.이 솔루션에서는 빠른 분할도 필요하지만 여러 가지 방법으로 빠른 곱셈을 통해 얻을 수 있습니다.

이미 답변을 수락한 것으로 알고 있습니다. 하지만 다음은 이 변환에 대한 오픈 소스 구현입니다.

  1. 데이비드 게이의dtoa()에서 합니다.dtoa.chttps://www.netlib.org/fp/dtoa.c .

  2. ___printf_fp()에 시대에/stdio-common/printf_fp.c파일을 Glibc(예: https://ftp.gnu.org/gnu/glibc/glibc-2.11.2.tar.gz, )에 저장합니다.

개 모두 됩니다.%f - 형유printf제가 다음에서 썼듯이:

부동 소수점 번호를 인쇄하는 데 많은 작업이 있었습니다.금색 표준은 최소 길이의 십진수 등가물을 인쇄하여 십진수 등가물을 다시 읽을 때, 읽기 중 반올림 모드가 무엇이든 간에 처음부터 동일한 부동 소수점 번호를 얻을 수 있습니다.여러분은 버거와 다비그의 훌륭한 논문에서 알고리즘에 대해 읽을 수 있습니다.

, 은 C#을 있습니다.double문자열로 정확하게 표현하기: http://www.yoda.arachsys.com/csharp/DoubleConverter.cs

한눈에 봐도 C로 이식하는 것은 그리 어렵지 않고 C++로 쓰는 것도 훨씬 쉬워 보입니다.

추가로 생각해 보면, 지수 위에서 루프를 하기 때문에 Jon의 알고리즘도 O(e^2)인 것으로 보입니다.그러나 이는 알고리즘이 O(log(n)^2)(여기서 n은 부동 소수점 번호)라는 것을 의미하며, 로그 제곱 시간보다 더 나은 시간에 기본 2에서 기본 10으로 변환할 수 있는지 확신할 수 없습니다.

부동 소수점 전문가가 아니기 때문에, 저는 잘 테스트된 오픈 소스 라이브러리를 사용하는 것을 선호합니다.

GNU MPFR은 좋은 것입니다.

MPFR 라이브러리는 정확한 반올림으로 다중 정밀 부동 소수점 계산을 위한 C 라이브러리입니다.MPFR의 주요 목표는 효율적이고 의미론이 잘 정의된 다중 정밀 부동 소수점 계산을 위한 라이브러리를 제공하는 것입니다.

sprintf 및 유사한 함수는 일반적으로 원래 부동 소수점 값을 고유하게 결정하기 위해 최대 수의 유의한 자릿수까지만 지정되며, 정확한 소수점 표현을 인쇄할 필요는 없습니다.

기본값보다 더 많은 숫자를 요청할 수 있습니다.

printf("%.100g\n", 0.1);

0.1000000000000000055511151231257827021181583404541015625.

만약 당신이 더 정확한 결과를 원한다면, 대신 고정점 수학을 사용하는 것은 어떻습니까?변환 속도가 빠릅니다.오류가 알려져 있으므로 해결할 수 있습니다.당신의 질문에 대한 정확한 대답이 아니라 당신을 위한 다른 생각입니다.

제 머리 속에서 먼저 지수를 이진 지수의 합으로 나눈 다음 모든 작업을 무손실로 처리하는 것이 어떻겠습니까?

예.

10^2 = 2^6 + 2^5 + 2^2

합계:

mantissa<<6 + mantissa<<5 + mantissa<<2

제 생각에 그것을 분해하는 것은 자릿수의 O(n)에 있을 것이고, 이동은 O(1)이고, 합은 O(n) 자리에 있을 것입니다...

물론 결과를 저장할 수 있을 만큼 큰 정수 클래스가 있어야 합니다.

알려주세요 - 저는 이것이 궁금합니다, 정말 생각하게 만들었습니다. :-)

세 가지 방법이 있습니다.

  1. 번호인으로 숫자 bin또는hex

    이것이 가장 정확한 방법입니다.선호합니다hex그것은 베이스에 더 가깝기 때문입니다.10를 들어 읽기 위해 예를 를예들어읽기위해위해▁for▁for▁reading/.F.8h = 15.5여기서 정확한 손실은 없습니다.

  2. dec 관련 만 해당됩니다.

    은 이은다포함수있숫의다미니합자만는것할음을을 가질 수 있는 만 의미합니다.1가능한 한 가까운 숫자로 표시됩니다.

    num정수 자릿수는 쉽고 정확합니다(정밀 손실 없음).

    // n10 - base 10 integer digits
    // n2  - base 2 integer digits
    n10=log10(2^n2)
    n10=log2(2^n2)/log2(10)
    n10=n2/log2(10)
    n10=ceil(n2*0.30102999566398119521373889472449)
    // if fist digit is 0 and n10 > 1 then n10--
    

    num소수 자릿수가 더 까다롭고 경험적으로 다음과 같은 것을 발견했습니다.

    // n10 - base 10 fract. digits
    // n2  - base 2 fract. digits >= 8
    n10=0; if (n02==8) n10=1;
    else if (n02==9) n10=2;
    else if (n02> 9)
        {
        n10=((n02-9)%10);
             if (n10>=6) n10=2;
        else if (n10>=1) n10=1;
        n10+=2+(((n02-9)/10)*3);
        }
    

    표를 n02 <-> n10그러면 당신은 그 상수를 볼 수 있습니다.0.30102999566398119521373889472449하지만, 가 여히존 지재만트, 8시때비터작는할더작것을 나타낼 수 합니다.0.1만족스러운 정밀도로 (0.85 - 1.15 입니다. 음의 기저 지수 때문입니다.2종속성은 선형이 아니라 패턴화됩니다.는 작은 카운트(bit count에됩니다.<=52) 가 클수록 사용 할 수 log10(2)또는1/log2(10) 그거야

    더 큰 비트 카운트의 경우 다음을 사용합니다.

    n10=7.810+(9.6366363636363636363636*((n02>>5)-1.0));
    

    하지만 그 공식은 32비트 정렬입니다!!!그리고 더 큰 비트 카운트 광고 오류도 있습니다.

    십진법 숫자의 이진 표현에 대한 추신 추가 분석

    0.1
    0.01
    0.001
    0.0001
    ...
    

    정확한 패턴 반복이 나타나 비트 수에 대한 관련 숫자의 정확한 수를 나타낼 수 있습니다.

    명확성을 위해:

    8 bin digits -> 1 dec digits
    9 bin digits -> 2 dec digits
    10 bin digits -> 3 dec digits
    11 bin digits -> 3 dec digits
    12 bin digits -> 3 dec digits
    13 bin digits -> 3 dec digits
    14 bin digits -> 3 dec digits
    15 bin digits -> 4 dec digits
    16 bin digits -> 4 dec digits
    17 bin digits -> 4 dec digits
    18 bin digits -> 4 dec digits
    19 bin digits -> 5 dec digits
    20 bin digits -> 6 dec digits
    21 bin digits -> 6 dec digits
    22 bin digits -> 6 dec digits
    23 bin digits -> 6 dec digits
    24 bin digits -> 6 dec digits
    25 bin digits -> 7 dec digits
    26 bin digits -> 7 dec digits
    27 bin digits -> 7 dec digits
    28 bin digits -> 7 dec digits
    29 bin digits -> 8 dec digits
    30 bin digits -> 9 dec digits
    31 bin digits -> 9 dec digits
    32 bin digits -> 9 dec digits
    33 bin digits -> 9 dec digits
    34 bin digits -> 9 dec digits
    35 bin digits -> 10 dec digits
    36 bin digits -> 10 dec digits
    37 bin digits -> 10 dec digits
    38 bin digits -> 10 dec digits
    39 bin digits -> 11 dec digits
    40 bin digits -> 12 dec digits
    41 bin digits -> 12 dec digits
    42 bin digits -> 12 dec digits
    43 bin digits -> 12 dec digits
    44 bin digits -> 12 dec digits
    45 bin digits -> 13 dec digits
    46 bin digits -> 13 dec digits
    47 bin digits -> 13 dec digits
    48 bin digits -> 13 dec digits
    49 bin digits -> 14 dec digits
    50 bin digits -> 15 dec digits
    51 bin digits -> 15 dec digits
    52 bin digits -> 15 dec digits
    53 bin digits -> 15 dec digits
    54 bin digits -> 15 dec digits
    55 bin digits -> 16 dec digits
    56 bin digits -> 16 dec digits
    57 bin digits -> 16 dec digits
    58 bin digits -> 16 dec digits
    59 bin digits -> 17 dec digits
    60 bin digits -> 18 dec digits
    61 bin digits -> 18 dec digits
    62 bin digits -> 18 dec digits
    63 bin digits -> 18 dec digits
    64 bin digits -> 18 dec digits
    

    그리고 마침내 잘라낸 숫자를 반올림하는 것을 잊지 마세요!!!즉, 관련된 마지막 숫자 뒤의 숫자가>=5 관련 숫자는 12" "0" "0" "0" "0" "0"이어야.+1그리고 만약 그것이 이미 있다면.9당신이 이전 자리로 가야만 하는 것보다 더...

  3. 정확한 값을 인쇄합니다.

    분수 이진수의 정확한 값을 인쇄하려면 분수만 인쇄n 위치가 있는 숫자n표현된 값이 이 값의 합이기 때문에 분수 비트의 수는 소수점 이하의 수보다 클 수 없습니다.numLSB의 소수 자릿수입니다.위의 항목(글머리 기호 #2)은 십진수를 저장하는 데 관련이 있습니다.float(또는 관련 소수만 인쇄).

    두 개의 정확한 값의 음의 거듭제곱...

    2^- 1 = 0.5
    2^- 2 = 0.25
    2^- 3 = 0.125
    2^- 4 = 0.0625
    2^- 5 = 0.03125
    2^- 6 = 0.015625
    2^- 7 = 0.0078125
    2^- 8 = 0.00390625
    2^- 9 = 0.001953125
    2^-10 = 0.0009765625
    2^-11 = 0.00048828125
    2^-12 = 0.000244140625
    2^-13 = 0.0001220703125
    2^-14 = 0.00006103515625
    2^-15 = 0.000030517578125
    2^-16 = 0.0000152587890625
    2^-17 = 0.00000762939453125
    2^-18 = 0.000003814697265625
    2^-19 = 0.0000019073486328125
    2^-20 = 0.00000095367431640625
    

    제이 의부 힘인적의 의 힘10 64비트에 한 값 입니다.doubles:

    10^+ -1 = 0.1000000000000000055511151231257827021181583404541015625
            = 0.0001100110011001100110011001100110011001100110011001101b
    10^+ -2 = 0.01000000000000000020816681711721685132943093776702880859375
            = 0.00000010100011110101110000101000111101011100001010001111011b
    10^+ -3 = 0.001000000000000000020816681711721685132943093776702880859375
            = 0.000000000100000110001001001101110100101111000110101001111111b
    10^+ -4 = 0.000100000000000000004792173602385929598312941379845142364501953125
            = 0.000000000000011010001101101110001011101011000111000100001100101101b
    10^+ -5 = 0.000010000000000000000818030539140313095458623138256371021270751953125
            = 0.000000000000000010100111110001011010110001000111000110110100011110001b
    10^+ -6 = 0.000000999999999999999954748111825886258685613938723690807819366455078125
            = 0.000000000000000000010000110001101111011110100000101101011110110110001101b
    10^+ -7 = 0.0000000999999999999999954748111825886258685613938723690807819366455078125
            = 0.0000000000000000000000011010110101111111001010011010101111001010111101001b
    10^+ -8 = 0.000000010000000000000000209225608301284726753266340892878361046314239501953125
            = 0.000000000000000000000000001010101111001100011101110001000110000100011000011101b
    10^+ -9 = 0.0000000010000000000000000622815914577798564188970686927859787829220294952392578125
            = 0.0000000000000000000000000000010001001011100000101111101000001001101101011010010101b
    10^+-10 = 0.00000000010000000000000000364321973154977415791655470655996396089904010295867919921875
            = 0.00000000000000000000000000000000011011011111001101111111011001110101111011110110111011b
    10^+-11 = 0.00000000000999999999999999939496969281939810930172340963650867706746794283390045166015625
            = 0.00000000000000000000000000000000000010101111111010111111111100001011110010110010010010101b
    10^+-12 = 0.00000000000099999999999999997988664762925561536725284350612952266601496376097202301025390625
            = 0.00000000000000000000000000000000000000010001100101111001100110000001001011011110101000010001b
    10^+-13 = 0.00000000000010000000000000000303737455634003709136034716842278413651001756079494953155517578125
            = 0.00000000000000000000000000000000000000000001110000100101110000100110100001001001011101101000001b
    10^+-14 = 0.000000000000009999999999999999988193093545598986971343290729163921781719182035885751247406005859375
            = 0.000000000000000000000000000000000000000000000010110100001001001101110000110101000010010101110011011b
    10^+-15 = 0.00000000000000100000000000000007770539987666107923830718560119501514549256171449087560176849365234375
            = 0.00000000000000000000000000000000000000000000000001001000000011101011111001111011100111010101100001011b
    10^+-16 = 0.00000000000000009999999999999999790977867240346035618411149408467364363417573258630000054836273193359375
            = 0.00000000000000000000000000000000000000000000000000000111001101001010110010100101111101100010001001101111b
    10^+-17 = 0.0000000000000000100000000000000007154242405462192450852805618492324772617063644020163337700068950653076171875
            = 0.0000000000000000000000000000000000000000000000000000000010111000011101111010101000110010001101101010010010111b
    10^+-18 = 0.00000000000000000100000000000000007154242405462192450852805618492324772617063644020163337700068950653076171875
            = 0.00000000000000000000000000000000000000000000000000000000000100100111001001011101110100011101001001000011101011b
    10^+-19 = 0.000000000000000000099999999999999997524592683526013185572915905567688179926555402943222361500374972820281982421875
            = 0.000000000000000000000000000000000000000000000000000000000000000111011000001111001001010011111011011011010010101011b
    10^+-20 = 0.00000000000000000000999999999999999945153271454209571651729503702787392447107715776066783064379706047475337982177734375
            = 0.00000000000000000000000000000000000000000000000000000000000000000010111100111001010000100001100100100100100001000100011b
    

    이제 64비트에 대해 관련 십진수로만 인쇄된 10의 음수 검정력(나는 이것에 더 익숙합니다.doubles:

    10^+ -1 = 0.1
    10^+ -2 = 0.01
    10^+ -3 = 0.001
    10^+ -4 = 0.0001
    10^+ -5 = 0.00001
    10^+ -6 = 0.000001
    10^+ -7 = 0.0000001
    10^+ -8 = 0.00000001
    10^+ -9 = 0.000000001
    10^+-10 = 0.0000000001
    10^+-11 = 0.00000000001
    10^+-12 = 0.000000000001
    10^+-13 = 0.0000000000001
    10^+-14 = 0.00000000000001
    10^+-15 = 0.000000000000001
    10^+-16 = 0.0000000000000001
    10^+-17 = 0.00000000000000001
    10^+-18 = 0.000000000000000001
    10^+-19 = 0.0000000000000000001
    10^+-20 = 0.00000000000000000001
    

    도움이 되길 바랍니다 :)

넌 아냐.가장 가까운 방법은 바이트를 버리는 것입니다.

언급URL : https://stackoverflow.com/questions/3215235/how-do-you-print-the-exact-value-of-a-floating-point-number

반응형