【问题标题】:Extra precision required for the implementation of trig functions with BigDecimal使用 BigDecimal 实现三角函数所需的额外精度
【发布时间】: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


【解决方案1】:

您可以将不在 -pi>x>=pi 中的数字减少到该范围。 sin(x) 的泰勒展开式随着 abs(x) 的增加而变得不那么准确,因此将 x 减小到这个范围将提高您对大数的准确度。

【讨论】:

  • 我知道。但即便如此,我仍然不知道,比如说,5 个额外的数字是否足够。 pi 的计算也是如此。要将值减小到该范围,我必须至少有一个同样精确的 pi 值。
  • 您可以尝试转换为度数,校正范围,然后转换回弧度,但这本身可能会导致一些校正错误。
  • 要获得精度,转换为度数仍需要具有相同精度的 pi。因此,对于 10000 的精度,我也必须在该范围内有一个 pi(精度加 5 - 或其他任何值 - 只是为了确定)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-04
  • 1970-01-01
  • 2021-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-05
相关资源
最近更新 更多