问题描述
我正在用JAVA中的sin
使BigDecimal
起作用,这是我所能做到的:
package taylorSeries;
import java.math.BigDecimal;
public class Sin {
private static final int cutOff = 20;
public static void main(String[] args) {
System.out.println(getSin(new BigDecimal(3.14159265358979323846264),100));
}
public static BigDecimal getSin(BigDecimal x,int scale) {
BigDecimal sign = new BigDecimal("-1");
BigDecimal divisor = BigDecimal.ONE;
BigDecimal i = BigDecimal.ONE;
BigDecimal num = null;
BigDecimal result = x;
//System.err.println(x);
do {
x = x.abs().multiply(x.abs()).multiply(x).multiply(sign);
i = i.add(BigDecimal.ONE);
divisor = divisor.multiply(i);
i = i.add(BigDecimal.ONE);
divisor = divisor.multiply(i);
num = x.divide(divisor,scale + cutOff,BigDecimal.ROUND_HALF_UP);
result = result.add(num);
//System.out.println("d : " + divisor);
//System.out.println(divisor.compareto(x.abs()));
System.out.println(num.setScale(9,BigDecimal.ROUND_HALF_UP));
} while(num.abs().compareto(new BigDecimal("0.1").pow(scale + cutOff)) > 0);
System.err.println(num);
System.err.println(new BigDecimal("0.1").pow(scale + cutOff));
return result.setScale(scale,BigDecimal.ROUND_HALF_UP);
}
}
它使用泰勒级数: picture of the fomular
每次迭代都会添加单项式x
,并且始终为负数。
问题是x
的绝对值越来越大,所以迭代永远不会结束。
编辑:
我对三角函数很感兴趣,从头开始编写了这段代码,现在我看到了很多幼稚的错误。
我的初衷是这样的:num
是x^(2k+1) / (2k+1)!
divisor
是(2k+1)!
i
是2k+1
dividend
是x^(2k+1)
因此,我用divisor
更新dividend
和i
并通过num
计算sign * dividend / divisor
并通过{{1}将其添加到result
}
因此,新的有效代码是:
result = result.add(num)
解决方法
-
new BigDecimal(double)
构造函数通常不是您要使用的东西;首先出现BigDecimal的全部原因是double
很奇怪:双精度数可以表示几乎2 ^ 64个唯一值,仅此而已-(几乎)2 ^ 64个不同的值以对数形式涂抹了,在0到1之间的所有可用数字中,约有四分之一从1到无穷大之间为四分之一,而另一半则相同,但为负数。3.14159265358979323846264
不是被祝福的数字之一。改用字符串构造函数-只需将"
符号抛在其周围即可。 -
每个循环,
sign
应该切换,好吧,签名。你不是那样做的。 -
在第一个循环中,您用
x = x.abs().multiply(x.abs()).multiply(x).multiply(sign);
覆盖了x,所以现在'x'的值实际上是-x^3
,而原始x的值是 gone 。在下一个循环中,您将重复此过程,因此,您肯定离预期的效果还差得远。解决方案-不要覆盖x。在整个计算中,您需要x。 最终确定(getSin(final BigDecimal x)
,以帮助自己。
制作另一个BigDecimal值,然后将其称为累加器或不累加器。它以x的副本开始。
每个循环都将x乘以两次,然后切换符号。这样,循环中的累加器第一次为-x^3
。第二次是x^5
。第三次是-x^7
,依此类推。
- 还有更多错误,但有时我只是用金汤匙为您提供功课。
我强烈建议您学习调试。调试很简单!您真正要做的就是跟着计算机一起走。您需要手工计算并仔细检查您所得到的(无论是表达式的结果还是while循环是否循环)与计算机所获得的相匹配。通过使用调试器进行检查,或者如果您不知道如何进行操作,请学习,如果不想,请添加大量System.out.println
语句作为调试辅助。您的期望值与计算机的运行状况不符吗?您发现了一个错误。可能是其中之一。
然后考虑将代码的一部分拼接起来,以便您可以更轻松地检查计算机的工作。
例如,在这里num
应该反映:
- 在第一个循环之前:
x
- 第一循环:
x - x^3/3!
- 第二个循环:
x - x^3/3! + x^5/5!
等。但是,如果您将这些部分分开,则调试起来会非常简单。您最好要:
- 第一个循环:3个独立的概念:
-1
,x^3
和3!
。 - 第二个循环:
+1
,x^5
和5!
。
调试非常简单。
通常,它还会导致更清晰的代码,因此,我建议您将这些单独的概念作为变量,进行描述,编写循环并测试它们是否在执行所需的操作(例如,使用sysouts或调试器来实际观察蓄能器值从x
跳到x^3
跳到x^5
-这很容易检查),最后将它们放在一起。
与仅“全部编写,运行它,意识到它不起作用,耸耸肩,扬起眉毛,过头去堆满溢出并祈祷某人的水晶球正在摇晃”相比,这是一种更好的代码编写方法。美好的一天,他们看到了我的问题。
,这些术语全为否定的事实不是 问题(尽管您必须使其交替出现才能获得正确的序列)。
术语幅度为x^(2k+1) / (2k+1)!
。分子确实在增长,但分母也在增长,并且经过k = x
之后,分母开始“获胜”,并且序列总是收敛。
无论如何,您应该将自己限制在较小的x
范围内,否则对于非常大的乘积,计算将非常漫长。
对于正弦的计算,总是从将参数减小到范围[0,π]
开始。更好的是,如果您共同开发余弦函数,则可以简化为[0,π/2]
。