如何在Java中获取三角函数的精确值

问题描述 投票:0回答:4

这不是thisthis的重复 我正在为 Android 开发一个计算器应用程序,过去 20-30 天我一直在网上搜索,但没有找到任何合理的答案。我还研究了很多关于浮点计算的论文。 我也尝试过 Math 和 StrictMath 库。

我尝试过以下值

Math.cos(Math.PI/4) result in 0.7071067811865476 which is correct answer

Math.cos(Math.PI/2) result in 6.123233995736766E-17 correct answer is 0

Math.cos(Math.PI) result in -1.0 which is correct

Math.cos((3*Math.PI)/2) result in -1.8369701987210297E-16 correct answer is 0

Math.cos(Math.PI*2) result in 1.0 which is correct



Math.sin(Math.PI/4) result in 0.7071067811865476 which is correct answer

Math.sin(Math.PI/2) result in 1 which is correct

Math.sin(Math.PI) result in 1.2246467991473532E-16 correct answer is 0

Math.sin((3*Math.PI)/2) result in -1 which is correct

Math.sin(Math.PI*2) result in -2.4492935982947064E-16 correct answer is 0



Math.tan(Math.PI/4) result in 0.999999999999999 correct answer is 1

Math.tan(Math.PI/2) result in 1.633123935319537E16 correct answer is NaN

Math.tan(Math.PI) result in -1.2246467991473532E-16 correct answer is 0

Math.tan((3*Math.PI)/2) result in 5.443746451065123E15 correct answer is NaN

Math.tan(Math.PI*2) result in -2.4492935982947064E-16 correct answer is 0

当我在 Stock Lollipop 中包含的 Google 官方计算器上尝试所有这些计算时,得出了除 tan((3*PI)/2) 和 tan(PI/2) 之外的所有正确答案

当我在我的 Casio fx-991 PLUS 上尝试所有这些计算时,所有答案都是正确的。

现在我的问题是“谷歌的计算器和卡西欧的计算器如何利用CPU有限的浮动精度得到正确的答案?”和“我怎样才能 达到相同的输出?”

java math floating-point-precision
4个回答
4
投票

我对您给出的许多“正确答案”值持怀疑态度。 sin(pi) 是 0,但 Math.PI 不是 pi,它是一个近似值。仅接近 PI 的正弦值不应为 0。用户如何输入值?如果用户输入小数点,有 16 位小数,他/她应该期望得到一些在小数点后 16 位的结果。如果用户要求 sin(10^-15) 并且您将输入更改为 0 并返回结果 0,那么您就无法通过计算

(sin(10^-15)-sin(0))/(10^-15-0)
来计算 sin x 在 0 处的数值导数。如果用户输入 Math.PI 之类的近似值并且您将输入更改为 pi,情况也是如此。

正如 Bryan Reilly 所回答的,您可以在将结果呈现给用户之前对结果进行舍入,这将避免显示 5*10^-15 这样的值而不是 0。

您可以将输入移动到接近 0 的范围。例如,对于大于 pi 或小于 -pi 的 x 值,您可以减去 2pi 的倍数以获得 [-pi,pi] 中的值。然后您可以使用三角恒等式将您需要的域进一步减少到 [0,pi/2]。例如,如果 x 在 [-pi,pi/2] 中,则使用 sin(x) = -sin(x+pi)。

如果任何舍入误差都是不可接受的,那么也许您应该制作一个符号计算器而不是浮点计算器。


2
投票

最有可能的是,如果结果小于 1.0E-14,Google 和 Casios 计算器就会简单地向下舍入。

如果数量太大也可以做类似的事情。

浮点不准确很难处理,但舍入是解决这些问题的最常见方法。

尽管您似乎知道幕后发生了什么,但这可能会对您有所帮助:

http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html


0
投票

也许他们使用类似Big Decimal Class

之类的东西

抱歉,我还不能提供简单的评论。

例如,昨天我创建了一个简单的 swing 应用程序,它将十进制转换为无符号 int 二进制,反之亦然。问题是,用户在文本框中输入的是要转换的字符串值,它们甚至很容易超过 Long.MAX_VALUE。

我想要的是用户在这两种情况下输入所需数量的数字,因此我使用了 Big Integer Class,它类似于 Big Decimal 但适用于整数值。使用它的结果允许用户输入令人难以置信的长度的字符串,并且我的程序以尽可能多甚至更多的数字输出转换,远远超过Long.MAX_VALUE。

但是,由于数学函数使用双精度数,因此我们仍然受限于“Big”类,这些类使用双精度数和字符串进行初始化。如果您有一个可以用字符串表示数字的应用程序(最大字符长度为 Integer.MAX_VALUE),那么“Big”类就很棒。然而,如果你想用 double 来初始化,你显然会受到 double 的约束。


0
投票

你找到答案了吗?我也遇到过这样的问题。我只解决了特殊点的问题。例如对于

如果 (x= 2kπ+π/2) cosx = 0 否则 cosx = Math.cos(x)

通过 if 语句我已经解决了问题。

© www.soinside.com 2019 - 2024. All rights reserved.