可能性があるように見えますが、(少なくともC99では)int
に適用できる長さ修飾子があります:%hhd
、%hd
、%ld
および%lld
意味signed char
、short
、long
およびlong long
。 double
に適用できる長さ修飾子もあります:%Lf
手段 long double
。
問題はなぜ彼らはfloat
を省略しましたか?パターンに従って、それは%hf
。
デフォルトの引数プロモーションのため。
printf()
は可変引数関数(...
のシグネチャ)、すべてのfloat
引数はdouble
に昇格されます。
C11§6.5.2.2関数呼び出し
6呼び出された関数を表す式の型がプロトタイプを含まない場合、整数の昇格が各引数に対して実行され、型が
float
の引数がdouble
に昇格されます。これらは、デフォルトの引数プロモーションと呼ばれます。7関数プロトタイプ宣言子の省略記号表記により、最後に宣言されたパラメーターの後で引数の型変換が停止します。デフォルトの引数の昇格は、末尾の引数に対して実行されます。
可変個引数関数を呼び出すときのデフォルトの引数プロモーションのため、float
値は、関数呼び出しの前に暗黙的にdouble
に変換されます、およびfloat
値をprintf
に渡す方法はありません。 float
値をprintf
に渡す方法がないため、float
値の明示的なフォーマット指定子は必要ありません。
そうは言っても、 AntoineL は、コメントで興味深いポイントを取り上げました。これは、%lf
(現在scanf
で引数タイプdouble *
に対応するために使用されています) C99の根拠 の42ページによると、C_89以前のタイプの同義語であった "long float
"をかつて意味していました。そのロジックによって、%f
がfloat
に変換されたdouble
値を表すことを意図していたことは意味があるかもしれません。
hh
およびh
の長さ修飾子に関して、%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
int
からchar
またはshort
へのナローイング変換の結果は特に明確には定義されていませんが、少なくとも実装ですdefinedは、この決定を実際に文書化するために実装が必要であることを意味します。
パターンに従うと、それは
%hf
であるはずです。
観察したパターンに従って、%hf
はfloat
の範囲外の値をfloat
に変換する必要があります。ただし、そのようなdouble
からfloat
への狭められた変換 結果は未定義の動作になります であり、unsigned float
などはありません。あなたが見るパターンは意味がありません。
正式に言うと、%lf
はlong double
引数を示しておらず、long double
引数を渡した場合 未定義の動作を呼び出すことになります 。 ドキュメント から次のように明示されています。
l
(エル)...次のa
、A
、e
、E
、f
、F
、g
、またはG
変換指定子。
他の誰もこれを手に入れていないことに驚いていますか? %lf
は、%f
と同様にdouble
引数を示します。 long double
を印刷する場合は、%Lf
(大文字)を使用します。
今後、printf
とscanf
の両方の%lf
はdouble
とdouble *
の引数に対応することになります... %f
は前述の理由により、デフォルトの引数の昇格のために例外的です。
...および%Ld
もlong
を意味しません。つまり、 undefined behaviour です。
ISO C11標準の_6.5.2.2 Function calls /6
_および_/7
_から、式のコンテキストでの関数呼び出しについて説明します(私の強調):
6 /呼び出された関数を表す式にプロトタイプを含まないタイプがある場合、整数の昇格が各引数で実行され、タイプfloatの引数が昇格されますこれらは、デフォルトの引数プロモーションと呼ばれます。
7 /呼び出された関数を表す式にプロトタイプが含まれる型がある場合、引数は、割り当てのように、対応するパラメーターの型に暗黙的に変換され、各パラメーターの型がの非修飾バージョンになります。宣言された型。関数プロトタイプ宣言子の省略表記は、最後に宣言されたパラメーターの後で引数の型変換を停止させます。 デフォルトの引数の昇格は、後続の引数に対して実行されます。
つまり、プロトタイプ内の_...
_の後のfloat
引数はdouble
に変換され、printf
ファミリーの呼び出しはそのように定義されます(_7.21.6.11
_ et seq):
_int fprintf(FILE * restrict stream, const char * restrict format, ...);
_
したがって、printf()
- family呼び出しで実際にfloatをreceive呼び出す方法はないため、特別なフォーマットを使用しても意味がありませんそのための指定子(または修飾子)。
scanf
にはfloat、double、またはlong doubleの個別のフォーマット指定子があるため、printf
および同様の関数が同様の方法で実装されなかった理由はわかりませんが、C/C++そして標準は結局終わった。
プロセッサと現在のモードによっては、プッシュまたはポップ操作の最小サイズに問題がある可能性がありますが、これは、ローカル変数または構造体の変数のデフォルトの配置と同様に、デフォルトのパディングで処理できた可能性があります。 Microsoftは80ビット(10バイト)のサポートを終了しましたlong double
sが16ビットコンパイラから32/64ビットコンパイラに移行したとき、long double
sはdouble
sと同じ(64ビット/ 8バイト)。必要に応じて、12バイト境界または16バイト境界までパディングすることができましたが、これは行われませんでした。
Fscanfの下のCの根拠を読むと、以下が見つかります。
C99の新機能:hhおよびllの長さ修飾子がC99に追加されました。 llは新しいlong long int型をサポートします。 hhは、文字型を他のすべての整数型と同じように扱う機能を追加します。これは、SCNd8などのマクロの実装に役立ちます(7.18を参照)。
そのため、hh
は、すべての新しいstdint.h
タイプのサポートを提供する目的で追加されたと考えられます。これは、長整数修飾子が小さな浮動小数点数ではなく小さな整数に追加された理由を説明できます。
C90にh
が一貫していないがhh
がなかった理由は説明されていません。 C90で指定されている言語は常に一貫しているわけではなく、そのように単純です。そして、それ以降のバージョンは不整合を継承しています。
%hhd
、%hd
、%ld
および%lld
がprintf
に追加され、フォーマット文字列がscanf
とより一貫したものになりました。デフォルトの引数の昇格のため、printf
は冗長です。
では、なぜfloat
に%hf
が追加されなかったのですか?それは簡単です。scanf
の動作を見ると、float
にはすでにフォーマット指定子があります。 %f
です。また、double
のフォーマット指定子は%lf
です。
その%lf
は、C99がprintf
に追加したものとまったく同じです。 C99より前は、%lf
の動作は未定義でした(標準の定義を省略したため)。 C99以降、これは%f
の同義語です。
Cが発明されたとき、すべての浮動小数点値は、計算で使用される前、または関数(含む)double
に渡される前に、共通の型(つまりprintf
)に変換されたため、 printf
は、浮動小数点型を区別します。
IEEE-754浮動小数点規格では、算術の効率と精度を高めるために、通常の64ビットdouble
よりも大きいがより高速に処理できる80ビットタイプを定義しました。意図は、a=b+c+d;
のような式が与えられた場合、すべてを80ビット型に変換し、3つの80ビット数値を足し合わせて、結果を64ビット型に変換する方がより速くて正確です。合計(b+c)
を64ビット型として計算し、それをd
に追加するよりも。
新しいタイプをサポートするために、ANSI Cは新しいタイプlong double
を定義しました。この実装では、新しい80ビットタイプまたは64ビットdouble
を参照できます。残念ながら、IEEE-754 80ビットタイプの目的は、すべての値がdouble
に昇格された方法で新しいタイプに自動的に昇格するはずでしたが、ANSIはそれを作成しましたしたがって、新しい型はprintf
または他の可変メソッドに他の浮動小数点型とは異なる方法で渡されるため、このような自動昇格は不可能になります。
その結果、Cの作成時に存在していた両方の浮動小数点型で同じ%f
形式指定子を使用できますが、後で作成されたlong double
には別の%Lf
形式指定子(- 大文字L
)。