代码之家  ›  专栏  ›  技术社区  ›  system PAUSE

java.lang.math.pi是否等于gcc的m_pi?

  •  6
  • system PAUSE  · 技术社区  · 15 年前

    我正在编码Java和C/C++中的几种参考算法。其中一些算法使用π。我希望每个算法的两个实现 完全相同的 结果,没有不同的舍入。到目前为止一直有效的一种方法是使用自定义的 pi 常数,在两种语言中完全相同,如3.14159。但是,在Java和GCC库中已经定义了高精度常量时,定义PI是愚蠢的。

    我花了一些时间编写快速测试程序,查看每个库的文档,并阅读浮点类型。但我无法说服自己,java. Lang.Mth.pi(或者java. Lang.StuttMax.pi)是或不等于Mypi。

    GCC 3.4.4(cygwin)math.h包含:

    #define M_PI            3.14159265358979323846
                                             ^^^^^
    

    但这

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

    生产

    3.14159265358979311600
                     ^^^^^
    

    这表明后5位数字是不可信的。

    同时,javadocs说java.lang.math.pi是:

    这个 double 接近于 任何其他 圆周率 ,的比率 圆的周长 直径。

    public static final double PI  3.141592653589793d
    

    它忽略了常量中有问题的最后五位数字。

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

    生产

    3.14159265358979300000
                     ^^^^^
    

    如果你在浮点数据类型方面有一些专业知识,你能让我相信这些库常数是完全相等的吗?或者说他们绝对不平等?

    8 回复  |  直到 15 年前
        1
  •  11
  •   S.Lott    15 年前

    注意以下几点。

    这两个数字与16位小数相同。差不多有48位是一样的。

    在ieee 64位浮点数中,所有的位都不是符号或指数。

    这个 #define M_PI 有21位;大约63位的精度,这对IEEE80位浮点值很好。

    我想你看到的是 M_PI 价值。

        2
  •  8
  •   JeeBee    15 年前

    您要做的是打印出pi值的原始位模式并进行比较。

    在Java中使用 http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Double.html#doubleToRawLongBits(double) 方法获取应打印为二进制文件的长值。

    Java 5给出:

    • 圆周率为3.141592653589793
    • 原始位是46142566552045848
    • 二进制为10000000001001000011111010101000100001010100010100011000

    在C语言中,你可以 double pi = M_PI; printf("%lld\n", pi); 要获得相同的64位整数:46142566552045848(感谢bruno)。

        3
  •  3
  •   Steve Schnepp    15 年前

    很难计算出相同的值, 即使起始值相同 .

    浮点计算结果有时不同于体系结构(例如,认为X86/PowerPC),从编译器到另一个(认为GCC/MS C++),甚至使用相同的编译器,但不同的编译选项。不总是,但有时(通常在舍入时)。通常情况下,问题被忽略的时间已经足够晚了(在多次迭代和许多舍入差之后再考虑)

    这使得跨平台的多玩家游戏很难同时计算游戏状态的每次迭代(每个节点只接收输入,而不是实际的数据结构)。

    因此,即使在同一种语言(C/C++)中,结果也可能不同,从Java VM到本地主机也可能不同。

    更新:

    我找不到我读到的来源,但我发现 paper by Sun 关于这件事。

    就像你自己回答的那样, java.lang.math.pi和gccs m_pi可以管理为具有相同的值 . 魔鬼隐藏在这些价值观的使用中。ieee没有指定数学函数(sin,cos,exp,…)的输出。因此它是 计算结果 这不一定相同。

        4
  •  2
  •   Community Romance    7 年前

    是的,它们是相等的,并且使用它们将确保相同算法的GCC和Java实现在同一基础上——至少和使用手工定义一样多。 pi 常数会 γ .

    一个警告,暗示 S. Lott ,即gcc实现必须保持 M_PI 在一个 double 数据类型,而不是 long double ,以确保等价性。Java和GCC都各自使用IEEE-74的64位十进制表示法。 双重的 数据类型。库值的字节表示(msb到lsb),表示为 双重的 ,可获得如下信息(感谢 JeeBee ):

    PixByth.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
    

    基本表现 MYPI (作为) 双重的 ) Math.PI 完全一样,一模一样。

    –如所述 Steve Schnepp ,数学函数(如sin、cos、exp等)的输出不能保证相同,即使这些计算的输入按位相同。

        5
  •  1
  •   benmmurphy    15 年前

    一个双精度数只有52位左右的符号,所以我认为它只能给你15个10位数的基数,这就解释了为什么你要求20位数时有5个零。

        6
  •  1
  •   Peter Lawrey    15 年前

    您可以使用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));
    }
    
        7
  •  -1
  •   MJC    15 年前

    使人想起在fortran中必须为pi取一个值。

    因为没有常数库,所以我要么使用 4*atan(1.)或acos(-1.)。

        8
  •  -3
  •   Roman    15 年前

    不,他们不平等,他们在记忆中有不同的表现。

    一般来说,当您想要比较两个浮点值时,您不能使用==(如果是这样,您就不能使用term'equals'进行操作)。你应该和epsilon比较。

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