6


0

java.lang.Math.PIはGCCのM_PIと同じですか?

私はJavaとC / Cの両方でいくつかの参照アルゴリズムをコーディングしています。 これらのアルゴリズムのいくつかはπを使用します。 各アルゴリズムの2つの実装で、丸めずに*同一*の結果が得られるようにします。 これまで一貫して機能してきたこれを行う1つの方法は、3.14159のように、両方の言語でまったく同じカスタム定義の `pi`定数を使用することです。 しかし、JavaライブラリとGCCライブラリの両方ですでに高精度の定数が定義されている場合、piを定義するのは愚かなことです。

簡単なテストプログラムを書いたり、各ライブラリのドキュメントを見たり、浮動小数点型について読んだりしました。 しかし、私はjava.lang.Math.PI(またはjava.lang.StrictMath.PI)がmath.hのM_PIと等しい、または等しくないことを自分自身に納得させることができませんでした。

GCC 3.4.4(cygwin)math.hの内容は以下のとおりです。

#define M_PI            3.14159265358979323846
                                         ^^^^^

でも、これ

printf("%.20f", M_PI);

作り出す

3.14159265358979311600
                 ^^^^^

これは、最後の5桁が信頼できないことを示唆しています。

一方、Javadocsはjava.lang.Math.PIは次のように言っています。

_ 円の円周と直径の比である_pi_に最も近い「double」値。 _

and

public static final double PI  3.141592653589793d

これは定数から疑わしい最後の5桁を省略します。

System.out.printf("%.20f\n", Math.PI);

作り出す

3.14159265358979300000
                 ^^^^^

浮動小数点データ型に関する専門知識がある場合、これらのライブラリ定数はまったく同じであると私に納得させることができますか? それとも彼らは間違いなく等しくないのですか?

8 Answer


11


以下に注意してください。

2つの数字は、小数点以下16桁まで同じです。 それはほぼ48ビットです。

IEEE 64ビット浮動小数点数では、符号や指数ではないすべてのビットがあります。

`#define M_PI`は21桁の数字です。これは約63ビットの精度です。これは、IEEE 80ビット浮動小数点値に適しています。

私が見ているのは、 `M_PI`値の中のビットの通常の切り捨てだと思います。


8


あなたがしたいことは、PI値の生のビットパターンをプリントアウトしてそれらを比較することです。

Javaでは、http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Double.html#doubleToRawLongBits(double)メソッドを使用して、バイナリとして印刷する必要がある長い値を取得します。

Java 5は、

  • PIは3.141592653589793です

  • 生ビットは4614256656552045848です。

  • バイナリは 100000000001001001000011111101101010100010001000010110100011000

Cでは、 `double pi = M_PI;とできます。 printf( "%lld \ n"、pi); `同じ64ビット整数を取得するには:4614256656552045848(ありがとうBruno)。


3


開始値が同じであっても、同じ値を計算することは非常に困難です。

浮動小数点演算の結果は、アーキテクチャごとに(たとえばx86 / PowerPCと思う)、コンパイラごと(GCC / MS Cと思う)と、コンパイラが同じでもコンパイルオプションが異なる場合もあります。 常にではありませんが、時々(通常は四捨五入時)。 通常、問題が遅すぎるまで気付かれずに済む程度に十分です(多数の反復および多数の四捨五入の違いがあると考えてください)。

これは、ゲーム状態の各繰り返しを同期的に計算するクロスプラットフォームのマルチプレイヤーゲームでは非常に困難になります(各ノードは実際のデータ構造ではなく入力のみを受け取ります)。

したがって、同じ言語(C / C)でも結果が異なる可能性がある場合は、Java VMからネイティブホストまで、結果も異なる可能性があります。

更新:

私が読んだソースを見つけることができませんが、私はその問題について Sunによる論文を見つけました。

あなた自身が答えたように、* java.lang.Math.PIとGCCのM_PIは同じ値*を持つように管理することができます。 悪魔はこれらの値の使い方を隠します。 IEEEは数学関数(sin、cos、exp、…​)の出力を指定していません。 したがって、必ずしも同じではないのは、*計算の出力*です。


2


そう、それらは同等であり、それらを使用することは、同じアルゴリズムのGCCとJava実装が同じ基盤にあることを保証するでしょう - 少なくとも手で定義された `pi`定数を使用することは

pi_bytes.c:

#include
#include
int main()
{
   double pi = M_PI;
   printf("%016llx\n", *((uint64_t*)&pi));
}

pi_bytes.java:

class pi_bytes
{
   public static void main(String[] a)
   {
      System.out.printf("%016x\n", Double.doubleToRawLongBits( Math.PI ) );
   }
}

両方を実行する:

$ gcc -lm -o pi_bytes pi_bytes.c && ./pi_bytes
400921fb54442d18

$ javac pi_bytes.java && java pi_bytes
400921fb54442d18

M_PI(` double`として)と `Math.PI`の根底にある表現は同じですが、それらの部分は異なります。

† - https://stackoverflow.com/questions/825559#825746 [Steve Schnepp]で述べたように、sin、cos、expなどの数学関数の出力 たとえそれらの計算への入力がビットごとに同一であっても、は同一であるという保証はありません。


1


ダブルはsignficandの52ビットのようなものしか持っていないので、私はあなたが20桁を要求するときあなたが5つのゼロを持っている理由を説明するであろう15ベース10桁の周りだけを与えると思います。


1


より正確にするためにBigDecimalを使用できます。

=新しいBigDecimalを( "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679" "8214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196" "4428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273" "7245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094" "3305727036575959195309218611738193261179310511854807446237996274956735188575272489122793818301194912" "9833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132" "0005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235" "4201995611212902196086403441815981362977477130996051870721134999999837297804995105973173281609631859"「502445945534690830264252230825334468503526193118817101000313783875288658プライベート静的最終BigDecimalのPI 7533208381420617177669147303" "5982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989" "3809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913151" "5574857242454150695950829533116861727855889075098381754637464939319255060400927701671139009848824012" "8583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912" "9331367702898915210475216205696602405803815019351125338243003558764024749647326391419927260426992279" "6782354781636009341721641219924586315030286182974555706749838505494588586926995690927210797509302955" "3211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000" "8164706001614524919217321721477235014144197356854816136115735255213347574184946843852332390739414333"「4547762416862518983569485562099219222184272550254256887671790494601653466804988627232791786 085784383" "8279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863" "0674427862203919494504712371378696095636437191728746776465757396241389086583264599581339047802759009" "9465764078951269468398352595709825822620522489407726719478268482601476990902640136394437455305068203" "4962524517493996514314298091906592509372216964615157098583874105978859597729754989301617539284681382" "6868386894277415599185592524595395943104997252468084598727364469584865383673622262609912460805124388" "4390451244136549762780797715691435997700129616089441694868555848406353422072225828488648158456028506" "0168427394522674676788952521385225499546667278239864565961163548862305774564980355936345681743241125")。

public static void main(String ... args)InterruptedException {System.out.println( "PI to" PI.scale() "の数字は" PI)です。 System.out.println( "PI ^ 2から" PI.scale() "の桁数は" PI.multiply(PI).setScale(PI.scale()、BigDecimal.ROUND_HALF_UP)); }


-1


Fortranでpiの値を取得しなければならないという思い出を呼び戻します。

定数のライブラリがなかったので、私は4 * atan(1。)かacos(-1。)を使いました。


-3


いいえ、彼らは平等ではありません、彼らは記憶の中で異なった表現をしています。

一般的に、2つの浮動小数点値を比較したいときは、==を使ってはいけません(そしてそうであれば、termin 'equals’で操作することはできません)。 イプシロンとの比較を使うべきです。

二重eps = 0.0000001。 if(Math.abs(Java_PI  -  Another_Pi)<= eps)System.out.println( "equals");