使用BigDecimal实现trig函数所需的额外精度

时间:2016-08-20 18:51:21

标签: java precision trigonometry bigdecimal

简介

我有兴趣为BigDecimal编写数学函数(实际上,也是为了... {4}用Delphi编写, 但这与此无关 - 在这个问题中,我使用Java BigDecimal,因为更多的人都知道它 我的BigDecimal非常相似。下面的测试代码是用Java编写的,并且在Delphi中工作正常 译文)。

我知道BigDecimal并不快,但它非常准确。我不想使用一些现有的Java BigDecimal数学库,尤其不是 因为这是我自己的BigDecimals类型(在 Delphi 中)。

作为如何实现trig函数的一个很好的例子,我找到了以下简单的例子(但我遗忘了哪里,对不起)。它显然使用 MacLaurin系列用于计算BigDecimal的余弦,具有给定的精度。

问题

这个精确度正是我的问题。下面的代码使用5的额外精度来计算结果,并且只在最后,它将其舍入到所需的精度。

我觉得5的额外精度很好,例如,目标精度高达50甚至更高,但不适用于cos(BigDecimal)精度更高(例如,1000位数或更多)。不幸的是,我无法找到验证这一点的方法(例如,使用在线非常准确的计算器)。

最后,我的问题是:我是对的 - 对于较大的数字而言,5可能还不够 - 如果是的话,我如何计算或估算所需的额外精度?

示例代码计算public class BigDecimalTrigTest { private List _trigFactors; private int _precision; private final int _extraPrecision = 5; // Question: is 5 enough? public BigDecimalTrigTest(int precision) { _precision = precision; _trigFactors = new Vector(); BigDecimal one = new BigDecimal("1.0"); BigDecimal stopWhen = one.movePointLeft(precision + _extraPrecision); System.out.format("stopWhen = %s\n", stopWhen.toString()); BigDecimal factorial = new BigDecimal(2.0); BigDecimal inc = new BigDecimal(2.0); BigDecimal factor = null; do { factor = one.divide(factorial, precision + _extraPrecision, BigDecimal.ROUND_HALF_UP); // factor = 1/factorial _trigFactors.add(factor); inc = inc.add(one); // factorial = factorial * (factorial + 1) factorial = factorial.multiply(inc); inc = inc.add(one); // factorial = factorial * (factorial + 1) factorial = factorial.multiply(inc); } while (factor.compareTo(stopWhen) > 0); } // sin(x) = x - x^3/3! + x^5/5! - x^7/7! + x^9/9! - ... = Sum[0..+inf] (-1^n) * (x^(2*n + 1)) / (2*n + 1)! // cos(x) = 1 - x^2/2! + x^4/4! - x^6/6! + x^8/8! - ... = Sum[0..+inf] (-1^n) * (x^(2*n)) / (2*n)! public BigDecimal cos(BigDecimal x) { BigDecimal res = new BigDecimal("1.0"); BigDecimal xn = x.multiply(x); for (int i = 0; i < _trigFactors.size(); i++) { BigDecimal factor = (BigDecimal) _trigFactors.get(i); factor = factor.multiply(xn); if (i % 2 == 0) { factor = factor.negate(); } res = res.add(factor); xn = xn.multiply(x); xn = xn.multiply(x); xn = xn.setScale(_precision + _extraPrecision, BigDecimal.ROUND_HALF_UP); } return res.setScale(_precision, BigDecimal.ROUND_HALF_UP); } public static void main(String[] args) { BigDecimalTrigTest bdtt = new BigDecimalTrigTest(50); BigDecimal half = new BigDecimal("0.5"); System.out.println("Math.cos(0.5) = " + Math.cos(0.5)); System.out.println("this.cos(0.5) = " + bdtt.cos(half)); } }

cos(.5) to 10000 digits

更新

Wolfram Alpha对body { /* Set the Serial counter to 0 */ counter-reset: Serial; } table { border-collapse: separate; } tr td:first-child:before { /* Increment the Serial counter */ counter-increment: Serial; /* Display the counter */ content: "Serial is: " counter(Serial); } 的测试(如@RC所评论的)给出了与我的测试代码相同精度的相同结果。也许5 足以作为额外的精确度。但我需要更多的测试才能确定。

1 个答案:

答案 0 :(得分:0)

您可以将-pi&gt; x&gt; = pi中的数字减少到该范围。随着abs(x)的增加,sin(x)的泰勒展开变得不那么准确,因此将x减小到这个范围会增加你对大数的准确性。