我想使用BigDecimal打印Pi,但事实证明它不准确
BigDecimal d = new BigDecimal(Math.PI);
System.out.println(d);
以上回答给了我" 3.141592653589793115997963468544185161590576171875" 但" 3.141592653589793"之后的数字;是不正确的
这是怎么回事?我可以用bigdecimal打印PI吗?
答案 0 :(得分:1)
是的,您可以使用BigDecimal
来打印π,但令人失望的是,最简单,最有效的方法可能是从Wolfram Mathematica这样的专用计算引擎复制数字并将其粘贴到Java中。 String
,然后使用它来初始化BigDecimal
。
Zabuzard在评论中建议进行实际计算π并将其放入BigDecimal
的过程。
在这里重要的是要了解π是无理数和先验数。这意味着无论我们将任何位或数字序列放在一起,都始终是有理近似。
Math.PI
中的近似值适合64位或16个十进制数字。为了使近似值值得投入BigDecimal
,它必须大于64位。也许是2,048位?
然后您需要一个好的公式,以在合理的时间内给出所需的精度。我选择了莱布尼兹公式。大错。我的初稿返回1.0,以确保它不会通过第一次测试。但是我可以很容易地从莱布尼兹公式开始,但仍未通过第一次测试。
package org.oeis.pi;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
public class BigDecimalPi {
private static final BigDecimal PI_QUARTERS;
private static final MathContext PREC_2048
= new MathContext(2048, RoundingMode.HALF_EVEN);
static {
BigDecimal number = BigDecimal.ZERO;
final BigDecimal two = BigDecimal.ONE.add(BigDecimal.ONE);
final BigDecimal negOne = BigDecimal.ONE.subtract(two);
BigDecimal powerOfNegOne = BigDecimal.ONE;
final BigDecimal precLim = new BigDecimal("10000000");
BigDecimal reciprocal;
for (BigDecimal i = BigDecimal.ONE; i.compareTo(precLim) < 0; i = i.add(two)) {
reciprocal = powerOfNegOne.divide(i, 1000, RoundingMode.CEILING);
number = number.add(reciprocal);
powerOfNegOne = powerOfNegOne.multiply(negOne);
}
PI_QUARTERS = number;
}
private static final BigDecimal FOUR = new BigDecimal("4");
public static BigDecimal pi() {
return PI_QUARTERS.multiply(FOUR);
}
}
要对此进行测试,请从您信任的来源获取数字。我使用Wolfram Mathematica,然后将输出粘贴到我的JUnit测试中。
package org.oeis.pi;
import java.math.BigDecimal;
import java.math.MathContext;
import org.junit.Test;
public class BigDecimalPiTest {
private static final String THOUSAND_PI_DIGITS = "3."
+ "1415926535897932384626433832795028841971693993751" // 1 to 49
+ "05820974944592307816406286208998628034825342117067" // 50 to 99
+ "98214808651328230664709384460955058223172535940812" // 100 to 149
+ "84811174502841027019385211055596446229489549303819" // 150 to 199
+ "64428810975665933446128475648233786783165271201909" // 200 to 249
+ "14564856692346034861045432664821339360726024914127" // 250 to 299
+ "37245870066063155881748815209209628292540917153643" // 300 to 349
+ "67892590360011330530548820466521384146951941511609" // 350 to 399
+ "43305727036575959195309218611738193261179310511854" // 400 to 449
+ "80744623799627495673518857527248912279381830119491" // 450 to 499
+ "29833673362440656643086021394946395224737190702179" // 500 to 549
+ "86094370277053921717629317675238467481846766940513" // 550 to 599
+ "20005681271452635608277857713427577896091736371787" // 600 to 649
+ "21468440901224953430146549585371050792279689258923" // 650 to 699
+ "54201995611212902196086403441815981362977477130996" // 700 to 749
+ "05187072113499999983729780499510597317328160963185" // 750 to 799
+ "95024459455346908302642522308253344685035261931188" // 800 to 849
+ "17101000313783875288658753320838142061717766914730" // 850 to 899
+ "35982534904287554687311595628638823537875937519577" // 900 to 949
+ "81857780532171226806613001927876611195909216420198" // 950 to 999
;
然后我让测试班从粘贴的数字中制作一个BigDecimal
。
private static final MathContext PRECISION_2048_BITS = new MathContext(2048);
private static final BigDecimal PI_FROM_THOUSAND_DIGITS
= new BigDecimal(THOUSAND_PI_DIGITS, PRECISION_2048_BITS);
然后是关键时刻。我写的测试容限为100个十进制数字。
@Test
public void testPi() {
System.out.println("pi");
System.out.println("Expecting " + PI_FROM_THOUSAND_DIGITS.toEngineeringString());
BigDecimal actual = BigDecimalPi.pi();
System.out.println("Got " + actual.toEngineeringString());
BigDecimal tolerance = new BigDecimal("1E-100");
BigDecimal delta = PI_FROM_THOUSAND_DIGITS.subtract(actual).abs();
System.out.println("Difference " + delta.toEngineeringString());
String assertionMessage = "Difference should be within tolerance";
assert delta.compareTo(tolerance) < 1 : assertionMessage;
}
}
对于仅返回1.0的初稿,测试失败了2.14159 ...,符合预期。但是,对于我第一次使用莱布尼兹公式的测试,测试几乎失败了0.01。当我将precLim
增加到10000000时,测试失败了199.9999999999980000000000000000000999999999999878 ... E-9,并且花费了将近24秒。
因此,要获得所需的精度,您将必须研究π的公式以找到更快的公式。数学术语是“收敛”。更快的收敛可能会转化为更快的算法。
经过所有这些努力,您可能会决定仅复制和粘贴数字就足够了。
再说一次,我读到某处天文学家只需要π到16位数字。这将意味着Math.PI
对于天文计算将是相当不错的。如果Fortran具有π的内置常数,则可能达到该精度。