在Java中,如何使用BigDecimal获得正弦值的精确结果?
我的目标是产生一个精确的正弦波。我的问题是,当我使用BigDecimal和StrictMath生成值时,一些零交叉错误,对称性被破坏 这里是一个频率为1、相位为0、振幅为1、时间为1秒、采样率为10的阵列(我将在本文后面发布代码): 对于准确性,[5]不应该为0吗?不应该(4=1)以及(2=3)、(9=6)和(7=8)吗 第二种情况下,相位等于StrictMath。PI/2.0似乎在[5]处产生精度:在Java中,如何使用BigDecimal获得正弦值的精确结果?,java,trigonometry,bigdecimal,Java,Trigonometry,Bigdecimal,我的目标是产生一个精确的正弦波。我的问题是,当我使用BigDecimal和StrictMath生成值时,一些零交叉错误,对称性被破坏 这里是一个频率为1、相位为0、振幅为1、时间为1秒、采样率为10的阵列(我将在本文后面发布代码): 对于准确性,[5]不应该为0吗?不应该(4=1)以及(2=3)、(9=6)和(7=8)吗 第二种情况下,相位等于StrictMath。PI/2.0似乎在[5]处产生精度: >[0] 1.0 >[1] 0.8090169943749475
>[0] 1.0
>[1] 0.8090169943749475
[2] 0.3090169943749475
[3] -0.3090169943749473
>[4] -0.8090169943749473
>[5] -1.0
[6] -0.8090169943749476
>[7] -0.3090169943749476
>[8] 0.3090169943749472
[9] 0.8090169943749472
在这种情况下,如果起点不太准确,[5]更准确,但同样,不应该(-4=1)以及(-2=3),(-9=6)和(-7=8)
所以我的问题是为什么会这样?为什么零交叉错误,而1和-1交叉正确?为什么正弦对称性被破坏了
以下是生成值的代码:
package Wave;
import java.math.BigDecimal;
/**
* @author Alexander Johnston
* Copyright 2019
* A class for sine waves
*/
public class SineWave extends Wave {
/** Creates a sine wave
* @param a as the amplitude of the sin wave from -amplitude to amplitude
* @param f as the frequency of the sine wave in Hz
* @param p as the phase of the sine wave
*/
public SineWave(BigDecimal a, BigDecimal f, BigDecimal p) {
this.a = a;
this.f = f;
this.p = p;
}
/* (non-Javadoc)
* @see waves.Wave#getSample(BigDecimal, float)
*/
public double[] getSample(BigDecimal t, float sr) {
int nsd;
BigDecimal nsdp = (new BigDecimal(Float.toString(sr)).multiply(t));
if(nsdp.compareTo(new BigDecimal(Integer.MAX_VALUE)) == -1) {
nsd = nsdp.intValue();
} else {
System.out.print("wave time is too long to fit in an array");
return null;
}
double[] w = new double[nsd];
for(int i = 0; i < w.length; i++) {
w[i] = a.multiply(new BigDecimal(StrictMath.sin(((new BigDecimal(2.0).multiply(new BigDecimal(StrictMath.PI)).multiply(f).multiply(new BigDecimal(i)).divide((new BigDecimal(Float.toString(sr))))).add(p)).doubleValue()))).doubleValue();
}
p = p.add(new BigDecimal(2.0).multiply(new BigDecimal(StrictMath.PI).multiply(f).multiply(t)));
return w;
}
}
主要方法是:
import java.math.BigDecimal;
import Wave.SineWave;
public class main {
public static void main(String[] args) {
BigDecimal a = new BigDecimal(1.0);
BigDecimal f = new BigDecimal(1.0);
BigDecimal p = new BigDecimal(0.0);
SineWave sw = new SineWave(a, f, p);
p = new BigDecimal(StrictMath.PI).divide(new BigDecimal(2.0));
SineWave swps = new SineWave(a, f, p);
BigDecimal t = new BigDecimal(1.0);
float sr = 10;
// The first array in this post
double [] swdns = sw.getSample(t, sr);
// The second array in this post
double [] swpsdns = swps.getSample(t, sr);
}
谢谢你花时间浏览我的帖子。非常感谢您的帮助。按照欧文的建议,我找到了一个适合我需要的图书馆。 当我将精度设置为1074位小数时,它有一个慷慨的许可证,并修复了我在这些特定数组中的问题,这是Java原语双精度值负指数的最大绝对值
再次感谢您对欧文的帮助
StrictMath.sin
不适用于大小数-仅适用于双精度-并且您不断地在大小数和双精度之间转换。这如何提高准确性?您需要找到一个库(或您自己的代码)以将sin
函数直接计算成所需精度的BigDecimal。我只在绝对必要的情况下使用转换,结果确实比不使用BigDecimal时提供了更好的精度。我要找一个图书馆。感谢您的帮助。您可以查看此问题以获得关于库的一些建议:我认为您看到的问题仍然源于双精度
的有限精度。如果你只看较少的小数,你所期望的所有等式都存在。例如,1.2246467991473532E-16
实际上非常非常接近于零-分数部分以15个零开始,然后才有一些非零数字,它们都源于预期的计算错误。我想我需要更高的精度,因为我正在努力产生声波。在上面的例子中,我使用Math.sin,相位计算中存在漂移,在一段时间内,当所有这些小的过零误差加在一起时,这将变得更加明显。也许我太挑剔了,在做了一些数学运算后,这对应于3分钟后的振幅0.00000000000097208748。很好,我找到了一个解决方案,但在我的项目的其他部分,我在准确性方面遇到了问题。
The wave class:
package Wave;
import java.math.BigDecimal;
/**
* @author Alexander Johnston
* Copyright 2019
* A class for waves to extend
*/
public abstract class Wave {
// Amplitude of the wave
protected BigDecimal a;
// Frequency of the wave in Hz
protected BigDecimal f;
// Phase of the wave, between 0 and (2*Math.PI)
protected BigDecimal p;
/** Generates a wave with with the correct amplitude
* @param t as the length of the wave in seconds
* @return An array with the wave generated with specified amplitude as amplitude over time
*/
abstract public double[] getSample(BigDecimal t, float sr);
}
import java.math.BigDecimal;
import Wave.SineWave;
public class main {
public static void main(String[] args) {
BigDecimal a = new BigDecimal(1.0);
BigDecimal f = new BigDecimal(1.0);
BigDecimal p = new BigDecimal(0.0);
SineWave sw = new SineWave(a, f, p);
p = new BigDecimal(StrictMath.PI).divide(new BigDecimal(2.0));
SineWave swps = new SineWave(a, f, p);
BigDecimal t = new BigDecimal(1.0);
float sr = 10;
// The first array in this post
double [] swdns = sw.getSample(t, sr);
// The second array in this post
double [] swpsdns = swps.getSample(t, sr);
}