부동 소수점 값의 정밀도를 유지하기위한 Printf 너비 지정자


103

문자열을 다시 스캔 할 때 원래 부동 소수점 값을 얻 printf도록 출력을 필요한 유효 자릿수 로 자동 형식화하는 부동 소수점 지정자에 적용 할 수 있는 너비 지정자가 있습니까?

예를 들어, 소수 자릿수 float의 정밀도로 a 를 인쇄한다고 가정 2합니다.

float foobar = 0.9375;
printf("%.2f", foobar);    // prints out 0.94

출력을 스캔 할 때 0.94원래 0.9375부동 소수점 값을 되 찾을 것이라는 표준 준수 보장이 없습니다 (이 예제에서는 아마도 그렇지 않을 것입니다).

에 전달 된 원래 값으로 다시 스캔 할 수 있도록 printf부동 소수점 값을 필요한 유효 자릿수 에 자동으로 인쇄 하는 방법을 알고 싶습니다 printf.

의 일부 매크로 float.h사용하여 전달할 최대 너비도출printf 있지만 필요한 유효 자릿수 또는 최소한 최대 너비 까지 자동으로 인쇄하는 지정자가 이미 있습니까?


4
@bobobobo 그래서 휴대용 접근 방식을 취하는 대신 공중에서 가정을 사용하는 것이 좋습니다.

1
@ H2CO3 아니요, "공중에서 나오는 가정"을 사용하지 않는 것이 좋습니다. printf( "%f", val );이미 휴대 가능하고 효율적이며 기본값 인 것을 사용 하는 것이 좋습니다 .
bobobobo 2013

2
@bobobobo 대답에 추가 할 수 있도록 정밀도가 지정되지 않은 경우 printf 문이 기본적으로 최대 정밀도 로 float 유형을 출력한다는 C99 표준의 절을 인용 할 수 있습니까?
Vilhelm Gray 2011

1
@VilhelmGray @chux가 들어가는 것처럼 특정 .NET의 실제 정밀도와 관련하여 꽤 복잡한 수학이 있습니다 double. 당신이로 double(1.0 아주 멀리) 매우 큰 도착, 실제로 얻을 덜 정확 (1.0보다 값 부분 이하) 소수 부분에. 귀하의 질문은 그것이 거짓 가정 (즉, 모든 것을 가지고 있기 때문에 당신은 정말 여기에서 만족스러운 답변을 가질 수 있도록 floatS / doubleS가 생성됩니다 같음)
bobobobo

2
@Vilhelm Gray C11dr 5.2.4.2.2 "... 소수 자릿수 n, p 기수 b 자릿수를 가진 모든 부동 소수점 숫자는 n 개의 소수 자릿수를 가진 부동 소수점 숫자로 반올림되고 변경없이 다시 되돌릴 수 있습니다. 값으로, p log10 bb는 10 ⎡1 + p log10 b⎤의 거듭 제곱입니다. 그렇지 않으면 FLT_DECIMAL_DIG 6 DBL_DECIMAL_DIG 10 LDBL_DECIMAL_DIG 10 ... "6,10,10이 최소값 입니다.
chux - 분석 재개 모니카

답변:


92

@Jens Gustedt 16 진수 솔루션을 권장합니다. % a를 사용합니다.

OP는 "최대 정밀도 (또는 최소한 최상위 소수까지)로 인쇄"를 원합니다.

간단한 예는 다음과 같이 7 분의 1을 인쇄하는 것입니다.

#include <float.h>
int Digs = DECIMAL_DIG;
double OneSeventh = 1.0/7.0;
printf("%.*e\n", Digs, OneSeventh);
// 1.428571428571428492127e-01

그러나 더 깊이 파헤쳐 보자 ...

수학적으로 답은 "0.142857 142857 142857 ..."이지만 유한 정밀도 부동 소수점 숫자를 사용하고 있습니다. IEEE 754 배정 밀도 바이너리 라고 가정 해 보겠습니다 . 따라서 OneSeventh = 1.0/7.0결과는 아래 값입니다. 또한 표현 가능한 이전 및 다음 double부동 소수점 숫자도 표시 됩니다.

OneSeventh before = 0.1428571428571428 214571170656199683435261249542236328125
OneSeventh        = 0.1428571428571428 49212692681248881854116916656494140625
OneSeventh after  = 0.1428571428571428 769682682968777953647077083587646484375

정확한 십진 표현을 인쇄 하는 double것은 제한된 용도로 사용됩니다.

C는 <float.h>우리를 돕기 위해 2 개의 매크로 계열을 가지고 있습니다.
첫 번째 세트는 문자열에서 10 진수로 인쇄 할 유효 자릿수이므로 문자열을 다시 스캔 할 때 원래 부동 소수점을 얻습니다. C 스펙의 최소값샘플 C11 컴파일러 와 함께 표시됩니다 .

FLT_DECIMAL_DIG   6,  9 (float)                           (C11)
DBL_DECIMAL_DIG  10, 17 (double)                          (C11)
LDBL_DECIMAL_DIG 10, 21 (long double)                     (C11)
DECIMAL_DIG      10, 21 (widest supported floating type)  (C99)

두 번째 세트는 문자열이 부동 소수점으로 스캔 된 다음 FP가 인쇄 될 수있는 유효 자릿수이며 여전히 동일한 문자열 표시를 유지합니다. C 스펙의 최소값샘플 C11 컴파일러 와 함께 표시됩니다 . 나는 C99 이전에 사용할 수 있다고 생각합니다.

FLT_DIG   6, 6 (float)
DBL_DIG  10, 15 (double)
LDBL_DIG 10, 18 (long double)

첫 번째 매크로 집합은 OP의 유효 자릿수 목표를 충족하는 것으로 보입니다 . 그러나이 매크로 는 항상 사용할 수있는 것은 아닙니다.

#ifdef DBL_DECIMAL_DIG
  #define OP_DBL_Digs (DBL_DECIMAL_DIG)
#else  
  #ifdef DECIMAL_DIG
    #define OP_DBL_Digs (DECIMAL_DIG)
  #else  
    #define OP_DBL_Digs (DBL_DIG + 3)
  #endif
#endif

"+ 3"은 이전 답변의 핵심이었습니다. 왕복 변환 문자열 -FP- 문자열 (C89에서 사용할 수있는 세트 # 2 매크로)을 알고 있다면 FP- 문자열 -FP (C89 이후에 사용 가능한 세트 # 1 매크로)의 자릿수를 어떻게 결정합니까? 일반적으로 3을 더하는 것이 결과입니다.

이제 인쇄 할 유효 자릿수는 <float.h>.

N 개의 유효 십진수 를 인쇄하려면 다양한 형식을 사용할 수 있습니다.

"%e"상기 정밀 필드는 자리수 리드 자리와 소수점. 그래서 - 1순서입니다. 참고 : 이것은 -1초기가 아닙니다.int Digs = DECIMAL_DIG;

printf("%.*e\n", OP_DBL_Digs - 1, OneSeventh);
// 1.4285714285714285e-01

으로 "%f"정밀 필드의 자리수 소수점은. 와 같은 숫자의 경우 모든 유효 자릿수 를 볼 OneSeventh/1000000.0필요 OP_DBL_Digs + 6가 있습니다 .

printf("%.*f\n", OP_DBL_Digs    , OneSeventh);
// 0.14285714285714285
printf("%.*f\n", OP_DBL_Digs + 6, OneSeventh/1000000.0);
// 0.00000014285714285714285

참고 : 대부분은 "%f". 소수점 뒤에 6 자리가 표시됩니다. 6은 숫자의 정밀도가 아니라 표시 기본값입니다.


1.428571428571428492127e-01이 아니라 1.428571428571428492127e-0 0 1 인 이유는 'e'뒤의 자릿수가 3이어야합니까?
user1024

12.12.5 부동 소수점 변환 은 기본 정밀도 %f가 6 이라고 말합니다 .
Jingguo Yao

1
@Jingguo Yao "정밀도는 '% f'의 소수점 문자 뒤에 오는 자릿수를 지정합니다."라는 참조에 동의합니다. "정밀도"라는 단어 는 수학적으로 사용 되지 않고 단순히 소수점 뒤의 자릿수를 정의하는 데 사용됩니다. 1234567890.123, 수학적으로 13 자리의 정밀도 또는 유효 숫자가 있습니다. 0.000000000123은 13이 아닌 3 자리의 수학적 정밀도를가집니다. 부동 소수점 숫자는 대수적으로 분포됩니다.이 답변은 유효 숫자 와 수학적 정밀도를 사용 합니다.
chux-Monica 복원

1
@Slipp D. Thompson "C 스펙의 최소값샘플 C11 컴파일러가 있습니다."
chux - 분석 재개 모니카

1
사실 당신 말이 맞습니다. 제 속임수는 1.0과 1.0eDBL_DIG 사이의 크기를 가진 값에만 유효합니다 "%f". "%e"당신이 보여준 것처럼 사용 하는 것은 물론 모든면에서 더 나은 접근 방식이며 효과적으로 괜찮은 대답입니다 (사용 "%a"가능한 경우 사용하는 것만 큼 좋지 않을 수도 있지만 물론 "%a"`DBL_DECIMAL_DIG가 있으면 사용할 수 있어야합니다). 나는 항상 정확히 최대 정밀도로 반올림하는 형식 지정자를 원했습니다 (하드 코딩 된 소수점 이하 6 자리 대신).
그렉 A. 우즈

66

부동 소수점 숫자를 무손실로 인쇄하는 짧은 대답 (NaN 및 Infinity를 제외하고 정확히 동일한 숫자로 다시 읽을 수 있음) :

  • 유형이 float 인 경우 printf("%.9g", number).
  • 유형이 double 인 경우 printf("%.17g", number).

%f소수점 이하 유효 자릿수 만 지정하고 작은 숫자를자를 수 있으므로 사용하지 마십시오 . 참고로, 마법의 숫자 9와 17에서 찾을 수 float.h있는 정의 FLT_DECIMAL_DIGDBL_DECIMAL_DIG.


6
%g지정자 를 설명해 주시겠습니까 ?
Vilhelm Gray

14
% g는 숫자가 작거나 큰 경우 (.00005가 아닌 1e-5) 지수 구문을 선호하고 후행 0 (1.00000이 아닌 1)을 건너 뛰고 정밀도를 위해 필요한만큼의 자릿수로 숫자를 인쇄합니다.
ccxvii 2014 년

4
@truthseeker IEEE 754 binary64 코드 를 표현하려면 실제로 최소 15 개의 유효 소수점 자리 를 인쇄해야 합니다. 그러나 이진수 (2,4,8 등에서)와 10 진수 (10,100,1000 등에서)의 정밀도 변화는 결코 같은 수 (1.0 제외)가 아니기 때문에 명확성은 17이 필요합니다. 예 : 2 개 double바로 위의 값은 0.1: 1.000_0000_0000_0000_2e-01, 1.000_0000_0000_0000_3e-01필요 17 자리 구분한다.
chux-Monica 복원

3
@chux-% .16g의 동작에 대해 착각했습니다. 그건 하지 1.000_0000_0000_0000_3e-01에서 1.000_0000_0000_0000_2e-01를 구별하여, 예를 들어 적절한. % .17g이 필요합니다.
돈 해치

1
@Don 해치 동의 "%.16g"불충분하고 "%.17g"그리고 "%.16e"충분하다. 의 세부 사항을 %g잘못 기억했습니다.
chux - 분석 재개 모니카

23

비트 (각각 16 진수 패턴)에만 관심이있는 경우 %a형식을 사용할 수 있습니다 . 이는 다음을 보장합니다.

기본 정밀도는 2 진법의 정확한 표현이 존재하고 그렇지 않으면 double 유형의 값을 구별 할 수있을만큼 충분히 큰 경우 값의 정확한 표현에 충분합니다.

나는 이것이 C99 이후로만 가능하다는 것을 덧붙여 야 할 것입니다.


16

아니요, 최대 정밀도로 부동 소수점을 인쇄하는 그러한 printf 너비 지정 자는 없습니다 . 이유를 설명하겠습니다.

float및 의 최대 정밀도 double가변적 이며 또는 의 실제 값 에 따라 다릅니다 .floatdouble

리콜 floatsign.exponent.mantissa 형식으로 double저장 됩니다. 이것은 큰 숫자보다 작은 숫자의 분수 구성 요소에 더 많은 비트가 사용 된다는 것을 의미합니다 .

여기에 이미지 설명 입력

예를 들어 float0.0과 0.1을 쉽게 구분할 수 있습니다.

float r = 0;
printf( "%.6f\n", r ) ; // 0.000000
r+=0.1 ;
printf( "%.6f\n", r ) ; // 0.100000

그러나 float사이의 차이의 아무 생각이 없습니다 1e271e27 + 0.1.

r = 1e27;
printf( "%.6f\n", r ) ; // 999999988484154753734934528.000000
r+=0.1 ;
printf( "%.6f\n", r ) ; // still 999999988484154753734934528.000000

이는 모든 정밀도 (가수 비트 수에 의해 제한됨)가 소수점 왼쪽 숫자의 많은 부분에 사용되기 때문입니다.

%.f수정 그냥 당신이 멀리로 플로트 번호에서 인쇄 할 얼마나 많은 소수 값 말한다 포맷이 간다. 사실 가능한 정확도 수의 크기에 따라 달라은 최대입니다 프로그래머 같이 처리 할 수. printf당신을 위해 그것을 처리 할 수 ​​없습니다 /하지 않습니다.


2
이것은 부동 소수점 값을 특정 소수 자릿수로 정확하게 인쇄하는 한계에 대한 훌륭한 설명입니다. 그러나 원래 선택한 단어가 너무 모호하다고 생각하므로 혼란을 해소하기 위해 "최대 정밀도"라는 용어를 사용하지 않도록 질문을 업데이트했습니다.
Vilhelm Gray 2011

여전히 인쇄중인 번호의 값에 따라 다릅니다.
bobobobo

3
이것은 부분적으로 사실이지만 질문에 대답하지 않으며 OP가 요구하는 것에 대해 혼란스러워합니다. 그는 a가 float제공 하는 유효 [십진수] 자릿수를 쿼리 할 수 ​​있는지 묻고 있으며 , 그러한 것이 없다고 주장합니다 (즉 FLT_DIG, 없음 ). 이것은 잘못된 것입니다.

@ H2CO3 아마도 당신은 내 게시물을 편집하고 반대 투표를해야 할 것입니다 (j / k). 이 답변은FLT_DIG 아무 의미가 없다고 합니다. 이 대답은 사용 가능한 소수 자릿수가 float 내부의 값에 따라 다르다고 주장합니다 .
bobobobo

1
형식 문자가 "f"여야한다고 가정하고 있습니까? 나는 그것이 필요하다고 생각하지 않습니다. 질문의 나의 독서는 OP가 찾고있는 것입니다 일부 의, 비 손실 왕복 생산의 printf 형식 지정자 대답 그래서 @ccxvii ( "% .9g는"플로트를 들어, "% .17g"두 배에 대한)이있는 좋은 것. 아마도 질문에서 "너비"라는 단어를 제거하면 더 나은 표현이 될 것입니다.
Don Hatch

11

다음의 매크로 <float.h>와 가변 너비 변환 지정자 ( ".*")를 사용하면됩니다.

float f = 3.14159265358979323846;
printf("%.*f\n", FLT_DIG, f);

2
@OliCharlesworth 마십시오 당신과 같이 의미 :printf("%." FLT_DIG "f\n", f);
Vilhelm 그레이에게

3
+1 그러나 이것은 가장 적합 %e하지 너무 잘 들어, %f:이 경우에만 인쇄 할 가치가 가까운 것을 알고있다 1.0.
Pascal Cuoq 2013 년

3
%e매우 작은 숫자에 대해 유효 숫자를 인쇄하고 인쇄 %f하지 않습니다. 예 : x = 1e-100. %.5f인쇄 0.00000(세차 운동의 총 손실). %.5e인쇄합니다 1.00000e-100.
chux-Monica 복원

1
@bobobobo 또한 "더 정확한 이유를 산출한다"는 점에서 틀 렸습니다. 이유 때문에FLT_DIG 정의 된 값으로 정의됩니다 . 6이면 float6 자리 이상의 정밀도를 보유 할 수 없기 때문 입니다. 를 사용하여 인쇄 %.7f하면 마지막 숫자는 의미가 없습니다. 반대 투표하기 전에 생각하십시오.

5
@bobobobo 아니요, 항상 6이 아니기 %.6f때문에 동등 FLT_DIG하지 않습니다. 그리고 누가 효율성에 신경을 쓰 나요? I / O는 이미 비용이 많이 들고, 한 자릿수 정도의 정밀도가 병목 현상을 일으키지 않습니다.

5

나는 인쇄 DBL_DECIMAL_DIG가 실제로 숫자의 이진 표현을 정확하게 보존 하는지 확인하기 위해 작은 실험을 실행합니다 . 내가 시도한 컴파일러와 C 라이브러리의 DBL_DECIMAL_DIG경우 실제로 필요한 자릿수이며 한 자릿수로 인쇄해도 심각한 문제가 발생 한다는 것이 밝혀졌습니다 .

#include <float.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

union {
    short s[4];
    double d;
} u;

void
test(int digits)
{
    int i, j;
    char buff[40];
    double d2;
    int n, num_equal, bin_equal;

    srand(17);
    n = num_equal = bin_equal = 0;
    for (i = 0; i < 1000000; i++) {
        for (j = 0; j < 4; j++)
            u.s[j] = (rand() << 8) ^ rand();
        if (isnan(u.d))
            continue;
        n++;
        sprintf(buff, "%.*g", digits, u.d);
        sscanf(buff, "%lg", &d2);
        if (u.d == d2)
            num_equal++;
        if (memcmp(&u.d, &d2, sizeof(double)) == 0)
            bin_equal++;
    }
    printf("Tested %d values with %d digits: %d found numericaly equal, %d found binary equal\n", n, digits, num_equal, bin_equal);
}

int
main()
{
    test(DBL_DECIMAL_DIG);
    test(DBL_DECIMAL_DIG - 1);
    return 0;
}

Microsoft의 C 컴파일러 19.00.24215.1 및 gcc 버전 7.4.0 20170516 (Debian 6.3.0-18 + deb9u1)으로 실행합니다. 소수를 하나 더 적게 사용하면 정확히 동일하게 비교되는 숫자의 수가 절반으로 줄어 듭니다. (저는 또한 rand()실제로 사용 된 대로 약 백만 개의 서로 다른 숫자를 생성 함을 확인했습니다 .) 다음은 자세한 결과입니다.

마이크로 소프트 C

17 자리 숫자로 999507 개의 값을 테스트했습니다. 999507은 숫자가 같고 999507은 이진수가 같음
16 자리 숫자로 999507 개의 값을 테스트했습니다. 545389는 숫자가 같고 545389는 이진수가 같음

GCC

17 자리 숫자로 999485 값을 테스트했습니다. 999485는 숫자가 같고 999485는 이진수가 같음
16 자리 숫자로 999485 값을 테스트했습니다. 545402는 수치가 같고 545402는 이진수가 같음

1
"Microsoft의 C 컴파일러로 실행"-> 해당 컴파일러에는 RAND_MAX == 32767. u.s[j] = (rand() << 8) ^ rand();모든 비트가 0 또는 1이 될 수있는 기회를 얻을 수 있도록 고려 하거나 기타를 고려하십시오 .
chux – Monica 복원

실제로 RAND_MAX는 32767이므로 제안이 정확합니다.
Diomidis Spinellis

1
@ chux-ReinstateMonica가 제안한대로 RAND_MAX를 처리하도록 게시물을 업데이트했습니다. 결과는 이전에 얻은 결과와 유사합니다.
Diomidis Spinellis

3

답변에 대한 내 의견 중 하나에서 나는 질문이 묻는 것과 거의 같은 방식으로 부동 소수점 값의 모든 유효 숫자를 십진수 형식으로 인쇄하는 방법을 오랫동안 원했다고 한탄했습니다. 나는 마침내 자리에 앉아 그것을 썼다. 완벽하지는 않으며 추가 정보를 인쇄하는 데모 코드이지만 대부분 내 테스트에서 작동합니다. 테스트를 위해 그것을 구동하는 전체 래퍼 프로그램의 사본을 원하시면 저에게 알려주십시오.

static unsigned int
ilog10(uintmax_t v);

/*
 * Note:  As presented this demo code prints a whole line including information
 * about how the form was arrived with, as well as in certain cases a couple of
 * interesting details about the number, such as the number of decimal places,
 * and possibley the magnitude of the value and the number of significant
 * digits.
 */
void
print_decimal(double d)
{
        size_t sigdig;
        int dplaces;
        double flintmax;

        /*
         * If we really want to see a plain decimal presentation with all of
         * the possible significant digits of precision for a floating point
         * number, then we must calculate the correct number of decimal places
         * to show with "%.*f" as follows.
         *
         * This is in lieu of always using either full on scientific notation
         * with "%e" (where the presentation is always in decimal format so we
         * can directly print the maximum number of significant digits
         * supported by the representation, taking into acount the one digit
         * represented by by the leading digit)
         *
         *        printf("%1.*e", DBL_DECIMAL_DIG - 1, d)
         *
         * or using the built-in human-friendly formatting with "%g" (where a
         * '*' parameter is used as the number of significant digits to print
         * and so we can just print exactly the maximum number supported by the
         * representation)
         *
         *         printf("%.*g", DBL_DECIMAL_DIG, d)
         *
         *
         * N.B.:  If we want the printed result to again survive a round-trip
         * conversion to binary and back, and to be rounded to a human-friendly
         * number, then we can only print DBL_DIG significant digits (instead
         * of the larger DBL_DECIMAL_DIG digits).
         *
         * Note:  "flintmax" here refers to the largest consecutive integer
         * that can be safely stored in a floating point variable without
         * losing precision.
         */
#ifdef PRINT_ROUND_TRIP_SAFE
# ifdef DBL_DIG
        sigdig = DBL_DIG;
# else
        sigdig = ilog10(uipow(FLT_RADIX, DBL_MANT_DIG - 1));
# endif
#else
# ifdef DBL_DECIMAL_DIG
        sigdig = DBL_DECIMAL_DIG;
# else
        sigdig = (size_t) lrint(ceil(DBL_MANT_DIG * log10((double) FLT_RADIX))) + 1;
# endif
#endif
        flintmax = pow((double) FLT_RADIX, (double) DBL_MANT_DIG); /* xxx use uipow() */
        if (d == 0.0) {
                printf("z = %.*s\n", (int) sigdig + 1, "0.000000000000000000000"); /* 21 */
        } else if (fabs(d) >= 0.1 &&
                   fabs(d) <= flintmax) {
                dplaces = (int) (sigdig - (size_t) lrint(ceil(log10(ceil(fabs(d))))));
                if (dplaces < 0) {
                        /* XXX this is likely never less than -1 */
                        /*
                         * XXX the last digit is not significant!!! XXX
                         *
                         * This should also be printed with sprintf() and edited...
                         */
                        printf("R = %.0f [%d too many significant digits!!!, zero decimal places]\n", d, abs(dplaces));
                } else if (dplaces == 0) {
                        /*
                         * The decimal fraction here is not significant and
                         * should always be zero  (XXX I've never seen this)
                         */
                        printf("R = %.0f [zero decimal places]\n", d);
                } else {
                        if (fabs(d) == 1.0) {
                                /*
                                 * This is a special case where the calculation
                                 * is off by one because log10(1.0) is 0, but
                                 * we still have the leading '1' whole digit to
                                 * count as a significant digit.
                                 */
#if 0
                                printf("ceil(1.0) = %f, log10(ceil(1.0)) = %f, ceil(log10(ceil(1.0))) = %f\n",
                                       ceil(fabs(d)), log10(ceil(fabs(d))), ceil(log10(ceil(fabs(d)))));
#endif
                                dplaces--;
                        }
                        /* this is really the "useful" range of %f */
                        printf("r = %.*f [%d decimal places]\n", dplaces, d, dplaces);
                }
        } else {
                if (fabs(d) < 1.0) {
                        int lz;

                        lz = abs((int) lrint(floor(log10(fabs(d)))));
                        /* i.e. add # of leading zeros to the precision */
                        dplaces = (int) sigdig - 1 + lz;
                        printf("f = %.*f [%d decimal places]\n", dplaces, d, dplaces);
                } else {                /* d > flintmax */
                        size_t n;
                        size_t i;
                        char *df;

                        /*
                         * hmmmm...  the easy way to suppress the "invalid",
                         * i.e. non-significant digits is to do a string
                         * replacement of all dgits after the first
                         * DBL_DECIMAL_DIG to convert them to zeros, and to
                         * round the least significant digit.
                         */
                        df = malloc((size_t) 1);
                        n = (size_t) snprintf(df, (size_t) 1, "%.1f", d);
                        n++;                /* for the NUL */
                        df = realloc(df, n);
                        (void) snprintf(df, n, "%.1f", d);
                        if ((n - 2) > sigdig) {
                                /*
                                 * XXX rounding the integer part here is "hard"
                                 * -- we would have to convert the digits up to
                                 * this point back into a binary format and
                                 * round that value appropriately in order to
                                 * do it correctly.
                                 */
                                if (df[sigdig] >= '5' && df[sigdig] <= '9') {
                                        if (df[sigdig - 1] == '9') {
                                                /*
                                                 * xxx fixing this is left as
                                                 * an exercise to the reader!
                                                 */
                                                printf("F = *** failed to round integer part at the least significant digit!!! ***\n");
                                                free(df);
                                                return;
                                        } else {
                                                df[sigdig - 1]++;
                                        }
                                }
                                for (i = sigdig; df[i] != '.'; i++) {
                                        df[i] = '0';
                                }
                        } else {
                                i = n - 1; /* less the NUL */
                                if (isnan(d) || isinf(d)) {
                                        sigdig = 0; /* "nan" or "inf" */
                                }
                        }
                        printf("F = %.*s. [0 decimal places, %lu digits, %lu digits significant]\n",
                               (int) i, df, (unsigned long int) i, (unsigned long int) sigdig);
                        free(df);
                }
        }

        return;
}


static unsigned int
msb(uintmax_t v)
{
        unsigned int mb = 0;

        while (v >>= 1) { /* unroll for more speed...  (see ilog2()) */
                mb++;
        }

        return mb;
}

static unsigned int
ilog10(uintmax_t v)
{
        unsigned int r;
        static unsigned long long int const PowersOf10[] =
                { 1LLU, 10LLU, 100LLU, 1000LLU, 10000LLU, 100000LLU, 1000000LLU,
                  10000000LLU, 100000000LLU, 1000000000LLU, 10000000000LLU,
                  100000000000LLU, 1000000000000LLU, 10000000000000LLU,
                  100000000000000LLU, 1000000000000000LLU, 10000000000000000LLU,
                  100000000000000000LLU, 1000000000000000000LLU,
                  10000000000000000000LLU };

        if (!v) {
                return ~0U;
        }
        /*
         * By the relationship "log10(v) = log2(v) / log2(10)", we need to
         * multiply "log2(v)" by "1 / log2(10)", which is approximately
         * 1233/4096, or (1233, followed by a right shift of 12).
         *
         * Finally, since the result is only an approximation that may be off
         * by one, the exact value is found by subtracting "v < PowersOf10[r]"
         * from the result.
         */
        r = ((msb(v) * 1233) >> 12) + 1;

        return r - (v < PowersOf10[r]);
}

나는 그것이 질문에 대한 답인지 아닌지 상관하지 않습니다. 이것은 정말 인상적입니다. 그것은 약간의 생각이 필요했고 인정되고 칭찬되어야합니다. 테스트를 위해 전체 코드를 (여기에 있든 그렇지 않든) 어떻게 든 포함한다면 좋을 것입니다. 그러나 그것이 없이도 이것은 정말 좋은 일입니다. 그것에 대해 +1하십시오!
Pryftan

0

제가 알기로, 잘 확산 할 수 있도록 알고리즘이 유효 숫자의 필요한 수에 출력되도록, 원래의 부동 소수점 값을 획득에서 문자열 다시 스캔하는 경우dtoa.c사용할 수 있습니다 다니엘 게이, 쓴 여기 (참조 NETLIB에를 또한 관련 논문 ). 이 코드는 예를 들어 Python, MySQL, Scilab 등에서 사용됩니다.

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