2016-09-23 11 views
1

私はしばしば、コマンドラインプログラム "printf" を使って、C stdioライブラリ関数が何をするかをテスト/予測しました。printf( "%。17g n"、.1)はどうすれば印刷できますか?

しかし、私が最近発見したことは(それ以上?)を動作しません - 少なくとも最近のLinux/Ubuntuのリリースで、 コマンドラインのprintf(組み込みのbashと/ usr/binに両方/ printf) は、C関数とは異なる結果を与えることがあります。そのような(明らかに) のpython、perlの、gnuplotの、または実際のCプログラムとして、私は 、C関数が何をするか確認したい場合、私は実際にCの関数を呼び出すいくつかの他のプログラムを実行しなければならないことを意味

。ここで

は、Linux上で、デモです:いくつかの他のプラットフォームで

bash$ uname -srvmpio 
    Linux 3.13.0-95-generiC#142-Ubuntu SMP Fri Aug 12 17:00:09 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux 
bash$ bash --version 
    GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu) 
    Copyright (C) 2013 Free Software Foundation, Inc. 
bash$ type printf 
    printf is a shell builtin 
bash$ printf "%.17g\n" .1 
    0.1 
bash$ /usr/bin/printf "%.17g\n" .1 
    0.1 
bash$ python -c 'print "%.17g" % .1' 
    0.10000000000000001 
bash$ perl -e 'printf("%.17g\n", .1);' 
    0.10000000000000001 
bash$ gnuplot -e 'print sprintf("%.17g", .1)' 
    0.10000000000000001 
bash$ echo -e '#include <stdio.h>\nint main(){printf("%.17g\\n",.1);}' | gcc -xc - -o /tmp/printf_.1 && /tmp/printf_.1 
    0.10000000000000001 

、例えば最新のMacBook、それらはすべて(私は常に正しい答えだと思ってきました)「0.10000000000000001」 を与える:

bash$ uname -srvmp 
    Darwin 15.6.0 Darwin Kernel Version 15.6.0: Thu Jun 23 18:25:34 PDT 2016; root:xnu-3248.60.10~1/RELEASE_X86_64 x86_64 i386 
bash$ bash --version 
    GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin15) 
bash$ type printf 
    printf is a shell builtin 
bash$ printf "%.17g\n" .1 
    0.10000000000000001 
bash$ /usr/bin/printf "%.17g\n" .1 
    0.10000000000000001 

私は、もちろん上記のコマンドがないことに驚いと失望していますすべて特定のプラットフォーム内で全く同じ出力を提供します。 私の質問です:いくつかの公式の仕様によると、正しい答えが1つあります、とにかく、バグレポートを提出する必要がありますか?

一部背景の事実:Cで

.1は、数学的に値正確にIEEE倍精度浮動小数点数 、意味:

x = 0.1000000000000000055511151231257827021181583404541015625 

を、隣接する表現可能な値は、次のとおり

x- = 0.09999999999999999167332731531132594682276248931884765625 
x+ = 0.10000000000000001942890293094023945741355419158935546875 

したがって、文字列表現「0.10000000000000001」と「0.1」は両方とも非ロスであるため、 それらが両方とも他の倍精度値よりもxに近いこと。 つまり、いずれかの文字列が指定されている場合、妥当な文字列から二重への変換関数は、 xを生成します。だから両方の答えはこの意味で同じように良いです。

ただし、0.10000000000000001は0.1よりxに近い。 私はいつも "0.10000000000000001"が正解であることを意味していると思っていましたが、 ですが、今は分かりません。実装依存の詳細なのでしょうか? 17どのような場合に使用される数字の右の数である、numeric_limitsのため マニュアルを参照してください理由の説明について

:: max_digits10:

http://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2005.pdf

+0

@ user3386109 "help printf"は、/ usr/bin/printfが行うことに加えて、いくつかの拡張機能を追加しました。 "man printf"は、/ usr/bin/printfはC printfが何をするかを示します。 C printfが正しい答えを持っていれば、それらはすべて確実に答えます。もしそうでなければ、私は同じ回答を与えるなら、本当に役に立つだろうという微妙な議論をしなければなりません。 )。 –

+0

単精度値(float)を比較しようとするべきではありません。正確ではありません。 –

+0

@ user3386109 "stdio"タグを削除して "bash"を追加しました。私はそれに同意しない。私の見解では、この質問は実際にはbashに関するものではありません。 bashの振る舞いは/ usr/bin/printfに委ねられています。これは私がよりよく振る舞い、指定されたプログラムです。 stdioはbashよりも関連性の高いタグです。 –

答えて

4

続きを約あります一般的にCで、bashまたはposixの仕様に適用される場合と適用されない場合があります。

printf(" %.17f\n", .1)は、少なくともDBL_DECIMAL_DIG正しいを印刷するを期待されています。 DBL_DECIMAL_DIGは、doubleの小数点テキスト表現からdoubleに戻って保証される精度で、同じ結果が得られます。これを行うには、少なくとも10桁と一般に17桁の数字が正しいです。これはOPが確実によく理解できるOPの状況です。

//0.09999999999999999167332731531132594682276248931884765625 
//0.1000000000000000055511151231257827021181583404541015625 
//0.10000000000000001942890293094023945741355419158935546875 
// 123456789
    0.10000000000000001 // DBL_DECIMAL_DIG is commonly 17 

問題があり、printf()は非常に正確に、このいずれかを行うためにを必要としないされます。したがって、printf()の品質は、0.1がいずれかのテキストを使用して正常に往復する可能性があるため、以下のいずれかを印刷した場合、わずかに許容可能とみなされます。

// 123456789
    0.10000000000000001 
    0.10000000000000000 

浮動小数点演算の精度 - であるとして(+、*、/)と浮動小数点結果を返す<math.h><complex.h>のライブラリ関数のは、実装が定義されます<stdio.h>,<stdlib.h>および<wchar.h>の浮動小数点内部表現と文字列表現の間の変換精度はです。実装には、精度が不明であることが記載されている場合があります。 6

C11dr§5.2.4.2.2

[編集]

doubleは、様々な精度、ベース、範囲の詳細を有することができます。簡単のため、この議論は、それよりもサンプル値は約0.1ではありませんが、0.10000000000000000555 ...

@user694733のコメントは有用であるが、まだそれは「正確でなければなりません使用しています「お勧め」の下に..です"。 printf("%.17f", .1)の場合は "0.10000000000000001"と指定していませんが。それは上記と一緒にOPの質問に答えるのを助けます: "に印刷する必要がありますか?"、 "0.10000000000000001" になります。その精度はEEE floating pointへの準拠を主張するCの実装についてはC.

によって定義された実装である。しかし、私は「0.10000000000000001」またはの結果「0.10000000000000000」が必要であることを確信していますが、それはCの質問を超えているとIEEE 754-xxxxに変換します。 IEEE floating point Character representationを読むと、緯度がdouble(実際にはbinary64) - >テキスト - >doubleの往復だけであるという適合する実装が可能です。これには少なくとも15桁の有効桁が必要で、17桁までかかることがあります。doubleへのテキストの変換以外は、15桁を超える数字は指定されていません。開始はdoubleです。


深い。 IEEE 754の場合、0.100000000000000005551115123...の値は、「間違って」「0.10000000000000001249000902 ...」までの何ものでも印刷でき、依然として正しく往復する可能性があります。

// 123456789
//0.1000000000000000055511151231257827021181583404541015625 
//0.10000000000000001249000902... half way 
//0.10000000000000001942890293094023945741355419158935546875 

注:IEEEが一定量(double 20)を越え有効数字を無視doubleとにテキストを変換することができます。


ショートIEEE答え:各種精度にXを印刷するとき、出力は以下の[C I]の範囲内にある必要があります。

123456789
A 0.099999999999999991673327315... preceding X 
B 0.099999999999999998612221219... half way 
C 0.099999999999999998613   half way rounded to 20 sig digits toward x 
D 0.099999999999999999    "%.17e" candidate 
E 0.10000000000000000    "%.17e", "%.17f" candidate 
X 0.100000000000000005551115123... 
G 0.10000000000000001    "%.17e", "%.17f" candidate 
H 0.10000000000000001249   half way rounded to 20 sig digits toward x 
I 0.100000000000000012490009027... half way 
J 0.100000000000000019428902930... following X 
+2

また、7.21.6.1 p13:* "の最も近い値について... の有効桁数がDECIMAL_DIGより大きいが、ソース値がDECIMAL_DIG桁で表される正確に の場合、結果は正確に である必要があります**ソース値は、隣接する小数桁L user694733

関連する問題