Program Tip

`printf`에`float`에 대한 지정자가 정의되지 않은 이유는 무엇입니까?

programtip 2020. 11. 5. 18:51
반응형

`printf`에`float`에 대한 지정자가 정의되지 않은 이유는 무엇입니까?


그것은 수 있었다처럼, 거기에 적용 할 수있는 길이 수정 (적어도 C99에)있는 모양 int: %hhd, %hd, %ld%lld평균 signed char, short, longlong long. 에 적용 할 수있는 길이 수정도있다 double: %Lf수단 long double.

질문은 생략 float했습니까? 패턴에 따라 %hf.


C 가변 함수 호출에서 모든 float인수는으로 승격 (즉 변환) double되므로 printfa 를 가져 와서 구현 내에서 가져 오는 데 double사용 va_arg(arglist, double)합니다.

과거 (C89 및 K & R C)에서는 모든 float인수가 double. 현재 표준은 명시적인 프로토 타입이있는 고정 된 arity 함수에 대한이 프로모션을 생략합니다. ABI구현의 호출 규칙 과 관련이 있으며 세부 정보가 설명되어 있습니다. 실제로 float값은 인수로 전달 될 때 종종 이중 부동 소수점 레지스터에로드되지만 세부 사항은 다를 수 있습니다. Linux x86-64 ABI 사양 을 예로 읽어 보세요.

또한 원하는대로 float출력 너비 (예 :)를 조정할 수 있고 에서 보다 훨씬 더 유용 (거의 필요) 하기 때문에 특정 형식 제어 문자열을 제공 할 실질적인 이유가 없습니다 .%8.5f%hdscanfprintf

그건 그렇고, 내가 이유를 추측 (에 생략 %hf지정 float-promoted에 double있는 콜러의 printf) 역사입니다 : 처음에, C는 시스템 프로그래밍 언어,하지 (포트란 아마도 1990 년대 후반까지 HPC에서 선호되었다)는 HPC의 일이었고, float그다지 중요하지 않았습니다. short메모리 소비를 낮추는 방법 인 것처럼 생각되었습니다 . 그리고 오늘날의 FPU는 (데스크톱 또는 서버 컴퓨터에서) float메모리를 적게 사용하기위한 수단으로 사용하지 않을 만큼 충분히 빠릅니다 . 기본적으로 모든 float것이 어딘가에 (아마도 FPU 또는 CPU 내부 ) double.

실제로, 귀하의 질문은 다음과 같이 의역 될 수 있습니다 : 왜 %hd존재 합니까 printf(기본적으로 쓸모없는 곳은 통과 할 때 printf얻는 것이지만 필요합니다!). 이유는 모르겠지만 시스템 프로그래밍보다 더 유용 할 것이라고 생각합니다.intshortscanf

당신이 얻을 수있는 다음 ISO C 표준을 로비 시간을 보낼 수 있다고 %hf인정 printf에 대한 float(승진 double에서 printf와 같이 호출 short-s가에 승진 int이중 정밀도 값이 행 아웃 때 정의되지 않은 동작으로) float-s 및 symetrically %hf에 의해 허용 scanf에 대한 float포인터. 행운을 빕니다.


의 때문에 기본 인수 프로모션 .

printf()가변 인수 함수 ( ...서명에서)이고 모든 float인수는로 승격됩니다 double.

C11 §6.5.2.2 함수 호출

6 호출 된 함수를 나타내는 표현식에 프로토 타입이 포함되지 않은 유형이있는 경우 각 인수에 대해 정수 승격이 수행되고 유형 float있는 인수 는로 승격됩니다 double. 이를 기본 인수 승격이라고합니다.

7 함수 프로토 타입 선언자의 생략 부호로 인해 마지막으로 선언 된 매개 변수 이후에 인수 유형 변환이 중지됩니다. 기본 인수 승격은 후행 인수에서 수행됩니다.


가변 함수를 호출 할 때 기본 인수 승격으로 인해 float값은double 함수 호출 전에 암시 적으로 변환되며 값을 에 전달할 방법이 없습니다floatprintf . float을 전달할 방법이 없기 때문에 printf에 대한 명시적인 형식 지정자가 필요하지 않습니다 float.

즉, 말로 미루어 보아, AntoineL가 있는 코멘트에서 흥미로운 점을 제기 %lf(현재에 사용되는 scanf인수 유형에 대응 double *한 번 "에 대한 서 수도) long float의 42 페이지에 따라, 사전 C89의 일 유형의 동의어이었다" C99 근거 . 그 논리함으로써, 의미 할 수도 있습니다 %fA의 서 의도 floatA를 변환 된 값을 double.


에 대해서 hhh길이를 수정, %hhu%hu이러한 형식 지정자에 대한 현재의 잘 정의 된 사용 사례를 : 당신은 큰의 최하위 바이트를 인쇄 할 수 있습니다 unsigned int또는 unsigned short예를 들어, 캐스트없이 :

printf("%hhu\n", UINT_MAX); // This will print (unsigned char)  UINT_MAX
printf("%hu\n",  UINT_MAX); // This will print (unsigned short) UINT_MAX

특히에서 축소 변환 어떤 정의되지 않은 intchar이상이 short발생합니다,하지만 적어도이다 구현 정의 구현이 사실이 결정을 문서화 할 필요가 의미.

패턴을 따라야합니다 %hf.

관찰 한 패턴 %hf에 따라 범위를 벗어난 값 floatfloat. 그러나에서 로의 이러한 종류의 축소 변환 double으로 float 인해 정의되지 않은 동작 이 발생하며 unsigned float. 당신이 본 패턴이 말이되지 않습니다.


공식적으로 정확하려면 %lf나타내지 않는 long double인수를하고 있었다 경우 전달하는 long double인수를 당신이 정의되지 않은 동작을 호출 할 것이다 . 문서 에서 다음같이 명시되어 있습니다 .

l(엘) ... 후속에 영향이없는 a, A, e, E, f, F, g, 또는 G변환 지정자.

아무도 이것에 대해 알아 차리지 않았다는 것이 놀랍습니까? 같은 인수를 %lf나타냅니다 . 를 인쇄 하려면 (대문자 ell)을 사용하십시오.double%flong double%Lf

이제부터는 대응 인수 %lf모두 대해 ... 앞서 언급 한 이유로 기본 인수 승격 때문에 예외적 이라는 것이 이해되어야합니다 .printfscanfdoubledouble *%f

...도 %Ld의미하지 않습니다 long. 이것이 의미하는 바는 정의되지 않은 동작 입니다.


는 ISO C11 표준에서 6.5.2.2 Function calls /6하고 /7, 표현 (내 강조)의 맥락에서 함수 호출을 논의 :

6 / 호출 된 함수를 나타내는 표현식에 프로토 타입이 포함되지 않은 유형이있는 경우 각 인수에 대해 정수 승격이 수행되고 float 유형의 인수는 double로 승격됩니다. 이를 기본 인수 승격이라고합니다.

7 / 호출 된 함수를 나타내는 표현식에 프로토 타입이 포함 된 유형이있는 경우 인수는 할당에 의해 해당 매개 변수 유형으로 암시 적으로 변환되며 각 매개 변수의 유형은 다음의 비정규 화 된 버전이됩니다. 선언 된 유형. 함수 프로토 타입 선언자의 줄임표 표기법으로 인해 마지막으로 선언 된 매개 변수 이후에 인수 유형 변환이 중지됩니다. 기본 인수 승격은 후행 인수에서 수행됩니다.

이 수단 모든 것을 float인수 후 ...프로토 타입이로 변환 double하고, printf통화의 가족이 정의되는 방법 ( 7.21.6.11등의 서열) :

int fprintf(FILE * restrict stream, const char * restrict format, ...);

따라서 printf()-family 호출이 실제로 float를 받을 수 있는 방법이 없기 때문에 특별한 형식 지정자 (또는 수정 자)를 사용하는 것은별로 의미가 없습니다.


fscanf 아래의 C 근거를 읽으면 다음을 찾을 수 있습니다.

C99의 새로운 기능 : hh 및 ll 길이 수정자가 C99에 추가되었습니다. ll은 새로운 long long int 유형을 지원합니다. hh는 문자 유형을 다른 모든 정수 유형과 동일하게 처리하는 기능을 추가합니다. 이는 SCNd8과 같은 매크로를 구현하는 데 유용 할 수 있습니다 (7.18 참조).

그래서 아마도 hh모든 새로운 stdint.h유형 에 대한 지원을 제공하기 위해 추가되었습니다 . 이것은 왜 길이 수정자가 작은 정수에 대해 추가되었지만 작은 부동에는 추가되지 않았는지 설명 할 수 있습니다.

C90은 일관성이 없었다 이유를 설명하지 h아니하지만, hh그래도. C90에 지정된 언어는 항상 일관성이없고 단순하지는 않습니다. 그리고 이후 버전은 불일치를 상속했습니다.


C가 발명되었을 때 모든 부동 소수점 값은 double계산에 사용되거나 함수 (포함)에 전달되기 전에 공통 유형 (예 :)으로 변환 printf되었으므로 printf부동 소수점 유형을 구분할 필요가 없습니다 .

산술 효율성과 정확성을 높이기 위해 IEEE-754 부동 소수점 표준은 일반 64 비트보다 크지 double만 더 빠르게 처리 할 수 있는 80 비트 유형을 정의했습니다 . 의도는 a=b+c+d;모든 것을 80 비트 유형으로 변환하고 세 개의 80 비트 숫자를 더한 다음 결과를 64 비트 유형으로 변환하는 것이 합계를 계산하는 것보다 더 빠르고 정확할 것 같은식이 주어 졌을 때였습니다. (b+c)64 비트 유형으로 추가 한 다음 d.

새로운 유형을 지원하기 위해 ANSI C long double는 새로운 80 비트 유형 또는 64 비트 구현을 참조 할 수 있는 새로운 유형 정의했습니다 double. 안타깝게도 IEEE-754 80 비트 유형 목적 은 모든 값이으로 승격 된 방식대로 자동으로 새 유형으로 승격되도록 하는 것이 었지만 doubleANSI는 새 유형이 전달되도록 printf하거나 다른 가변 메서드와 다르게 다른 부동 소수점 유형으로 인해 이러한 자동 승격을 유지할 수 없습니다.

결과적으로 C를 만들 때 존재했던 두 부동 소수점 형식은 동일한 %f형식 지정자를 사용할 수 있지만 long double나중에 만든 %Lf형식 지정 자는 다른 형식 지정자 ( 대문자 사용 L )가 필요합니다.


%hhd, %hd, %ld and %lld were added to printf to make the format strings more consistent with scanf, even though they are redundant for printf because of the default argument promotions.

So why wasn't %hf added for float? That's easy: looking at scanf's behaviour, float already has a format specifier. It's %f. And the format specifier for double is %lf.

That %lf is exactly what C99 added to printf. Prior to C99, the behaviour of %lf was undefined (by omission of any definition in the standard). As of C99, it's a synonym for %f.


Considering scanf has separate format specifiers for float, double, or long double, I don't see why printf and similar functions weren't implemented in a similar way, but that's how C / C++ and the standards ended up.

There can be an issue with the minimum size for a push or pop operation depending on the processor and the current mode, but this could have been handled with default padding, similar to default alignment of local variables or variables in a structure. Microsoft dropped support for 80 bit (10 byte) long doubles when it went from 16 bit to 32 / 64 bit compilers, now treating long doubles the same as doubles (64 bit / 8 byte). They could have padded them to 12 or 16 byte boundaries as needed, but this wasn't done.

참고URL : https://stackoverflow.com/questions/32517932/why-wasnt-a-specifier-for-float-defined-in-printf

반응형