【发布时间】:2016-12-27 17:05:46
【问题描述】:
简介
我有兴趣为BigDecimal 编写数学函数(实际上,也为
my own BigDecimal type 用德尔福写的,
但这在这里无关紧要-在这个问题中,我使用Java的BigDecimal,因为更多人知道它并且
我的BigDecimal 非常相似。下面的测试代码是用 Java 编写的,运行良好,在 Delphi 中同样运行良好
翻译)。
我知道BigDecimal 速度不快,但非常准确。我不想使用一些现有的 Java BigDecimal 数学库,尤其是
因为这也是我自己的BigDecimal 类型(在Delphi 中)。
作为如何实现三角函数的一个很好的例子,我找到了以下简单的例子(但我忘了在哪里,抱歉)。它显然使用 MacLaurin 系列以给定精度计算 BigDecimal 的余弦。
问题
这个精度正是我的问题。下面的代码使用 5 的额外精度来计算结果,并且仅在最后四舍五入到所需的精度。
我感觉 5 的额外精度对于高达 50 甚至更高的目标精度来说是可以的,但对于精度更高(比如 1000 位或更多)的BigDecimals 则不行.不幸的是,我找不到验证这一点的方法(例如,使用极其精确的在线计算器)。
最后,我的问题是:我说得对吗——对于更大的数字来说,5 可能还不够——如果是的话,我该如何计算或估计所需的额外精度?
示例代码计算cos(BigDecimal):
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));
}
}
更新
使用 Wolfram Alpha 对cos(.5) to 10000 digits 进行的测试(正如@RC 评论的那样)给出了与我的测试代码相同的精度相同的结果。也许 5 作为额外的精度就足够了。但我需要更多测试才能确定。
【问题讨论】:
-
wolfram alpha 对于 cos 来说非常精确,请参阅 wolframalpha.com/input/?i=cos(12)+to+1000+digits
-
啊,谢谢,我会尝试用 Wolfram Alpha 检查我的结果。好提示!
-
只是一个想法:如果您进行符号计算,您可以懒惰地评估(无限)系列,将它们组合起来,每个系列都有一个错误精度,并且可能会收到更快的结果。使用 java 8 lambdas。
-
Hmmm... wolframalpha.com/input/?i=cos(0.5)+to+1000+digits (并设置弧度)给我的输出与我的测试代码完全相同,精度为 1000,所以在这个例子中,5 就足够了。必须尝试更多的数字和许多不同的值。我假设输入值也不应该离 0 太远。
-
@Joop:感谢您的建议,但正如我所写,这也应该可以翻译成 Delphi,并使用 BigDecimal。
标签: java precision trigonometry bigdecimal