Java Math.cos()方法在需要时不返回0

Java Math.cos()方法在需要时不返回0,java,trigonometry,Java,Trigonometry,在Windows7PC上使用Java(不确定这是否重要)并对应该返回0的值(如pi/2)调用Math.cos()会返回较小的值,但除非我有误解,否则这些较小的值远远大于0的1 ulp Math.cos(Math.PI/2) = 6.123233995736766E-17 Math.ulp(Math.cos(Math.PI/2)) = 1.232595164407831E-32 这是否在1 ulp范围内,而我只是感到困惑?这是一个可以接受的包装方法来解决这个小错误吗 public static

在Windows7PC上使用Java(不确定这是否重要)并对应该返回0的值(如pi/2)调用Math.cos()会返回较小的值,但除非我有误解,否则这些较小的值远远大于0的1 ulp

Math.cos(Math.PI/2) = 6.123233995736766E-17
Math.ulp(Math.cos(Math.PI/2)) = 1.232595164407831E-32
这是否在1 ulp范围内,而我只是感到困惑?这是一个可以接受的包装方法来解决这个小错误吗

public static double cos(double a){
    double temp = Math.abs(a % Math.PI);
    if(temp == Math.PI/2)
        return 0;
    return Math.cos(a);
}

不要忘记,
Math.PI/2
是一个近似值。它不完全是pi/2,因此
cos(Math.pi/2)
的结果不完全是0
Math.cos
可能会返回一个相当精确的余弦版本,该余弦是通过计算
Math.PI/2

返回的精确值,您不应该将
=
与双精度一起使用。您必须始终在误差范围内进行操作。如果你问我10-17是很精确的。Ulp数字10-32的精度仅为10-17个数量级的两倍,因为2.220446049250313E-16是100个数量级的精度。

这是一个常见错误,当您开始时,此链接对原因进行了非常技术性的讨论。


但在最简单的形式中,正如我们不能用十进制精确表示1/3一样,有些值不能用二进制精确表示。大多数数学运算的行为都没有精确定义,因此操作系统和CPU可能很重要。如果您想要行为被严格定义的数学操作(可以说更容易排除故障),那么您想要使用StrictMath,而不是math(当然StrictMath操作可能会更慢,因为它们无法使用CPU中可用的硬件加速操作)。math.cos()只是StrictMath.cos()的包装器,这本身就是一个本机函数。@Slartibartfsat:+1,完全正确。你比我快。误差应该至少是ulp(PI/2)的两倍,因为这是PI/2的不精确性。cos在这一点上有一个-1的导数,因此PI/2的不精确性会反映在结果中,加上cos的不精确性。通常我同意你的观点,但我想将我的例外情况限制在尽可能少的情况下,因此如果输入与Math.PI/2有偶数/非常/稍微的偏差,那么我会让它进行本机计算,但是在特殊情况下,它是Math.PI/2或者它的一个倍数,我希望它是零。一些有限的测试表明它是有效的,至少在我的电脑上是如此。这个推理可以吗,或者我应该继续检查一个范围吗?非常正确,但这对我来说似乎很直观,如果Math.PI是最接近PI的双精度,那么Math.PI/2应该是最接近PI/2的双精度。因为根据cos()的定义,Pi/2和3Pi/2是零,当这些数字的最接近的双近似值传递给Math.cos()时,它也应该是零。也许这是彻头彻尾的糟糕,(这是我问题的症结所在),但这对我来说是直观的。