在Java中,如何使用BigDecimal获得正弦值的精确结果?

在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

我的目标是产生一个精确的正弦波。我的问题是,当我使用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  
[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);
        }