2009-05-05 13 views
5

私は、JavaとC/C++の両方でいくつかの参照アルゴリズムをコーディングしています。これらのアルゴリズムの一部はπを使用します。それぞれのアルゴリズムの2つの実装で、と同じの結果が得られるようにしたいと思います。今まで一貫してこれを実行した方法の1つは、カスタム定義のpi定数を使用することです。この定数は、両方の言語で正確に同じです(3.14159など)。しかし、すでにJavaとGCCの両方のライブラリで定義されている高精度定数がある場合、piを定義するのは馬鹿げています。java.lang.Math.PIはGCCのM_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); 

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

3.14159265358979311600 
       ^^^^^ 

を生成します。

一方のJavadocはjava.lang.Math.PIであることを言う:PI、そのに対する円の 円周の比率に より近い他のある

double値直径は です。

と定数から疑問の最後の5桁を省略し

public static final double PI 3.141592653589793d 

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

あなたは浮動小数点データ型にいくつかの専門知識を持っている場合は、これらのライブラリの定数が正確に同じであることを私に納得させることができますか?

3.14159265358979300000 
       ^^^^^ 

を生成しますかそれとも、彼らは間違いなく平等ではないのですか?

答えて

2

はい、それらは等しく、それらを使用すると、同じアルゴリズムのGCCおよびJava実装は同じ立場–上だろう&ダガーハンド定義pi定数を使用するのと少なくとも同じくらいであることを保証します。S. Lottによって示唆

1つの警告は、GCCの実装は等価性を確保するために、long doubledoubleデータ型でM_PIを保持し、かつてはならないことです。 JavaとGCCの両方が、それぞれのdoubleデータ型のIEEE-754の64ビット10進表記を使用しているようです。ライブラリー値のバイト単位の表現は、(MSBがLSBに)、doubleとして表さ、(JeeBeeのおかげで)次のように求めることができる。

pi_bytes.c:

#include <math.h> 
#include <stdio.h> 
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_PIdoubleとして)とMath.PIの再呼び出しは、そのビットまで同一です。

&dagger; – Steve Schneppで示されるように、sin、cos、expなどの数学関数の出力は、それらの計算への入力がビットごとに同一であっても同一であることが保証されていません。

8

PI値の生ビットパターンを印刷して比較します。

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

のJava 5が得られます。

  • PIは3です。4614256656552045848(感謝ブルーノ):141592653589793
  • 生のビットは、同じ64ビット整数を取得するためにdouble pi = M_PI; printf("%lld\n", pi);を行うことができ、4614256656552045848
  • バイナリはCで100000000001001001000011111101101010100010001000010110100011000

です。

+3

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

+1

ありがとうブルーノは、かなり元の質問に答える。 – JeeBee

-3

いいえ、それらは等しくない、彼らはメモリ内で異なるプレゼンテーションを持っています。

一般に、2つの浮動小数点値を比較する場合は、==は使用しないでください(そうであれば、termin 'equals'で操作できません)。イプシロンとの比較を行うべきです。

double eps = 0.0000001; 
if (Math.abs (Java_PI - Another_Pi) <= eps) 
    System.out.println ("equals"); 
1

ダブルsignficandの52ビットは、私が唯一あなたが20桁を求めるときは、5ゼロを持っている理由を説明するだろう周りに15台を10桁与えることを考えるようにだけ持っています。

11

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

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

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

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

あなたが見ていると思うのは、M_PI値のビットの通常の切り捨てです。

+0

+1極端な精度の時間デルタ測定を長時間行うと、この問題に正確に遭遇する。 15桁の数字は64ビットの倍数で得られます。 – basszero

3

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

浮動小数点計算の結果は、あるコンパイラから別のコンパイラ(GCC/MS C++と考える)と同じコンパイラでもコンパイルオプションが異なる場合でもアーキテクチャとは異なる場合があります(x86/PowerPCなど)。必ずしもそうではありませんが、時には(通常は四捨五入)。多くの場合、多くの反復と多くの多くの丸めの違いを考えると、問題が気付かれなくなるまでには十分です。

これは、gamestateの各反復を同期して計算するクロスプラットフォームのマルチプレイヤーゲーム(各ノードは実際のデータ構造ではなく、入力のみを受け取ります)。

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

更新:

私は私が読んソースを見つけることができませんが、私は問題にpaper by Sunを見つけました。

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

+0

これは一般的なケースでは本当の問題ですが、ここではそうではありません。質問に記載されているように、「今まで一貫して機能してきたこの方法の1つは、3.14159など、両方の言語でまったく同じカスタム定義のpi定数を使用することです。 –

+0

参照リンクが追加されました。 –

1

あなたが好きな、より精度のためのBigDecimalを使用することができます。

private static final BigDecimal PI = new BigDecimal(
"3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679" + 
    "8214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196" + 
    "4428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273" + 
    "7245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094" + 
    "3305727036575959195309218611738193261179310511854807446237996274956735188575272489122793818301194912" + 
    "9833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132" + 
    "0005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235" + 
    "4201995611212902196086403441815981362977477130996051870721134999999837297804995105973173281609631859" + 
    "5024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303" + 
    "5982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989" + 
    "3809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913151" + 
    "5574857242454150695950829533116861727855889075098381754637464939319255060400927701671139009848824012" + 
    "8583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912" + 
    "9331367702898915210475216205696602405803815019351125338243003558764024749647326391419927260426992279" + 
    "6782354781636009341721641219924586315030286182974555706749838505494588586926995690927210797509302955" + 
    "3211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000" + 
    "8164706001614524919217321721477235014144197356854816136115735255213347574184946843852332390739414333" + 
    "4547762416862518983569485562099219222184272550254256887671790494601653466804988627232791786085784383" + 
    "8279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863" + 
    "0674427862203919494504712371378696095636437191728746776465757396241389086583264599581339047802759009" + 
    "9465764078951269468398352595709825822620522489407726719478268482601476990902640136394437455305068203" + 
    "4962524517493996514314298091906592509372216964615157098583874105978859597729754989301617539284681382" + 
    "6868386894277415599185592524595395943104997252468084598727364469584865383673622262609912460805124388" + 
    "4390451244136549762780797715691435997700129616089441694868555848406353422072225828488648158456028506" + 
    "0168427394522674676788952521385225499546667278239864565961163548862305774564980355936345681743241125" 
); 

public static void main(String... args) throws InterruptedException { 
    System.out.println("PI to " + PI.scale() + " digits is " + PI); 
    System.out.println("PI^2 to " + PI.scale() + " digits is " + 
      PI.multiply(PI).setScale(PI.scale(), BigDecimal.ROUND_HALF_UP)); 
} 
+1

ええ、ありがとう。そしてGCCのために? –

-1

は、Fortranでパイの値を取得することの思い出を思い出させます。

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

関連する問題