web-dev-qa-db-ja.com

`float`の指定子が` printf`で定義されなかったのはなぜですか?

可能性があるように見えますが、(少なくともC99では)intに適用できる長さ修飾子があります:%hhd%hd%ldおよび%lld意味signed charshortlongおよびlong longdoubleに適用できる長さ修飾子もあります:%Lf 手段 long double

問題はなぜ彼らはfloatを省略しましたか?パターンに従って、それは%hf

69
skyking

デフォルトの引数プロモーションのため。

printf()は可変引数関数(...のシグネチャ)、すべてのfloat引数はdoubleに昇格されます。

C11§6.5.2.2関数呼び出し

6呼び出された関数を表す式の型がプロトタイプを含まない場合、整数の昇格が各引数に対して実行され、型がfloatの引数がdoubleに昇格されます。これらは、デフォルトの引数プロモーションと呼ばれます。

7関数プロトタイプ宣言子の省略記号表記により、最後に宣言されたパラメーターの後で引数の型変換が停止します。デフォルトの引数の昇格は、末尾の引数に対して実行されます。

15
Yu Hao

可変個引数関数を呼び出すときのデフォルトの引数プロモーションのため、float値は、関数呼び出しの前に暗黙的にdoubleに変換されます、およびfloat値をprintfに渡す方法はありません。 float値をprintfに渡す方法がないため、float値の明示的なフォーマット指定子は必要ありません。

そうは言っても、 AntoineL は、コメントで興味深いポイントを取り上げました。これは、%lf(現在scanfで引数タイプdouble *に対応するために使用されています) C99の根拠 の42ページによると、C_89以前のタイプの同義語であった "long float"をかつて意味していました。そのロジックによって、%ffloatに変換された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であるはずです。

観察したパターンに従って、%hffloatの範囲外の値をfloatに変換する必要があります。ただし、そのようなdoubleからfloatへの狭められた変換 結果は未定義の動作になります であり、unsigned floatなどはありません。あなたが見るパターンは意味がありません。


正式に言うと、%lflong double引数を示しておらず、long double引数を渡した場合 未定義の動作を呼び出すことになりますドキュメント から次のように明示されています。

l(エル)...次のaAeEfFg、またはG変換指定子。

他の誰もこれを手に入れていないことに驚いていますか? %lfは、%fと同様にdouble引数を示します。 long doubleを印刷する場合は、%Lf(大文字)を使用します。

今後、printfscanfの両方の%lfdoubledouble *の引数に対応することになります... %fは前述の理由により、デフォルトの引数の昇格のために例外的です。

...および%Ldlongを意味しません。つまり、 undefined behaviour です。

10
autistic

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呼び出す方法はないため、特別なフォーマットを使用しても意味がありませんそのための指定子(または修飾子)。

5
paxdiablo

scanfにはfloat、double、またはlong doubleの個別のフォーマット指定子があるため、printfおよび同様の関数が同様の方法で実装されなかった理由はわかりませんが、C/C++そして標準は結局終わった。

プロセッサと現在のモードによっては、プッシュまたはポップ操作の最小サイズに問題がある可能性がありますが、これは、ローカル変数または構造体の変数のデフォルトの配置と同様に、デフォルトのパディングで処理できた可能性があります。 Microsoftは80ビット(10バイト)のサポートを終了しましたlong doublesが16ビットコンパイラから32/64ビットコンパイラに移行したとき、long doublesはdoublesと同じ(64ビット/ 8バイト)。必要に応じて、12バイト境界または16バイト境界までパディングすることができましたが、これは行われませんでした。

2
rcgldr

Fscanfの下のCの根拠を読むと、以下が見つかります。

C99の新機能:hhおよびllの長さ修飾子がC99に追加されました。 llは新しいlong long int型をサポートします。 hhは、文字型を他のすべての整数型と同じように扱う機能を追加します。これは、SCNd8などのマクロの実装に役立ちます(7.18を参照)。

そのため、hhは、すべての新しいstdint.hタイプのサポートを提供する目的で追加されたと考えられます。これは、長整数修飾子が小さな浮動小数点数ではなく小さな整数に追加された理由を説明できます。

C90にhが一貫していないがhhがなかった理由は説明されていません。 C90で指定されている言語は常に一貫しているわけではなく、そのように単純です。そして、それ以降のバージョンは不整合を継承しています。

2
Lundin

%hhd%hd%ldおよび%lldprintfに追加され、フォーマット文字列がscanfとより一貫したものになりました。デフォルトの引数の昇格のため、printfは冗長です。

では、なぜfloat%hfが追加されなかったのですか?それは簡単です。scanfの動作を見ると、floatにはすでにフォーマット指定子があります。 %fです。また、doubleのフォーマット指定子は%lfです。

その%lfは、C99がprintfに追加したものとまったく同じです。 C99より前は、%lfの動作は未定義でした(標準の定義を省略したため)。 C99以降、これは%fの同義語です。

2
user743382

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)。

2
supercat