日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

java中怎样定义实数_Java Math 类中的新功能,第 1 部分: 实数

發(fā)布時(shí)間:2024/9/30 java 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java中怎样定义实数_Java Math 类中的新功能,第 1 部分: 实数 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在這篇由兩部分組成的文章中,Elliotte Rusty Harold 與您一起探討經(jīng)典 java.lang.Math 類中的“新”功能。第 1 部分主要討論比較單調(diào)的數(shù)學(xué)函數(shù)。第 2 部分將探討專為操作浮點(diǎn)數(shù)而設(shè)計(jì)的函數(shù)。

有時(shí)候您會(huì)對(duì)一個(gè)類熟悉到忘記了它的存在。如果您能夠?qū)懗?java.lang.Foo 的文檔,那么 Eclipse 將幫助您自動(dòng)完成所需的函數(shù),您無需閱讀它的 Javadoc。例如,我使用 java.lang.Math(一個(gè)我自認(rèn)為非常了解的類)時(shí)就是這樣,但令我吃驚的是,我最近偶然讀到它的 Javadoc —— 這可能是我近五年來第一次讀到,我發(fā)現(xiàn)這個(gè)類的大小幾乎翻了一倍,包含 20 種我從來沒聽說過的新方法。看來我要對(duì)它另眼相看了。

Java? 語言規(guī)范第 5 版向 java.lang.Math(以及它的姊妹版 java.lang.StrictMath)添加了 10 種新方法,Java 6 又添加了 10 種。在本文中,我重點(diǎn)討論其中的比較單調(diào)的數(shù)學(xué)函數(shù),如 log10 和 cosh。在第 2 部分,我將探討專為操作浮點(diǎn)數(shù)(與抽象實(shí)數(shù)相反)而設(shè)計(jì)的函數(shù)。

抽象實(shí)數(shù)(如 π 或 0.2)與 Java double 之間的區(qū)別很明顯。首先,數(shù)的理想狀態(tài)是具有無限的精度,而 Java 表示法把數(shù)限制為固定位數(shù)。在處理非常大和非常小的數(shù)時(shí),這點(diǎn)很重要。例如,2,000,000,001(二十億零一)可以精確表示為一個(gè) int,而不是一個(gè) float。最接近的浮點(diǎn)數(shù)表示形式是 2.0E9 — 即兩億。使用 double 數(shù)會(huì)更好,因?yàn)樗鼈兊奈粩?shù)更多(這是應(yīng)該總是使用 double 數(shù)而不是 float 數(shù)的理由之一);但它們的精度仍然受到一定限制。

計(jì)算機(jī)算法(Java 語言和其他語言的算法)的第二個(gè)限制是它基于二進(jìn)制而不是十進(jìn)制。1/5 和 7/50 之類的分?jǐn)?shù)可用十進(jìn)制精確表示(分別是

0.2 和 0.14),但用二進(jìn)制表示時(shí),就會(huì)出現(xiàn)重復(fù)的分?jǐn)?shù)。如同 1/3 在用十進(jìn)制表示時(shí),就會(huì)變?yōu)?0.3333333……以 10

為基數(shù),任何分母僅包含質(zhì)數(shù)因子 5 和 2 的分?jǐn)?shù)都可以精確表示。以 2 為基數(shù),則只有分母是 2

的乘方的分?jǐn)?shù)才可以精確表示:1/2、1/4、1/8、1/16 等。

這種不精確性是迫切需要一個(gè) math 類的最主要的原因之一。當(dāng)然,您可以只使用標(biāo)準(zhǔn)的 + 和 * 運(yùn)算符以及一個(gè)簡單的循環(huán)來定義三角函數(shù)和其他使用泰勒級(jí)數(shù)展開式的函數(shù),如清單 1 所示:

清單 1. 使用泰勒級(jí)數(shù)計(jì)算正弦

public class SineTaylor {

public static void main(String[] args) {

for (double angle = 0; angle <= 4*Math.PI; angle += Math.PI/8) {

System.out.println(degrees(angle) + "/t" + taylorSeriesSine(angle)

+ "/t" + Math.sin(angle));

}

}

public static double degrees(double radians) {

return 180 * radians/ Math.PI;

}

public static double taylorSeriesSine(double radians) {

double sine = 0;

int sign = 1;

for (int i = 1; i < 40; i+=2) {

sine += Math.pow(radians, i) * sign / factorial(i);

sign *= -1;

}

return sine;

}

private static double factorial(int i) {

double result = 1;

for (int j = 2; j <= i; j++) {

result *= j;

}

return result;

}

}

開始運(yùn)行得不錯(cuò),只有一點(diǎn)小的誤差,如果存在誤差的話,也只是最后一位小數(shù)不同:

0.0 0.0 0.0

22.5 0.3826834323650897 0.3826834323650898

45.0 0.7071067811865475 0.7071067811865475

67.5 0.923879532511287 0.9238795325112867

90.0 1.0000000000000002 1.0

但是,隨著角度的增加,誤差開始變大,這種簡單的方法就不是很適用了:

630.0000000000003 -1.0000001371557132 -1.0

652.5000000000005 -0.9238801080153761 -0.9238795325112841

675.0000000000005 -0.7071090807463408 -0.7071067811865422

697.5000000000006 -0.3826922100671368 -0.3826834323650824

這里使用泰勒級(jí)數(shù)得到的結(jié)果實(shí)際上比我想像的要精確。但是,隨著角度增加到 360 度、720 度(4 pi 弧度)以及更大時(shí),泰勒級(jí)數(shù)就逐漸需要更多條件來進(jìn)行準(zhǔn)確計(jì)算。java.lang.Math 使用的更加完善的算法就避免了這一點(diǎn)。

泰勒級(jí)數(shù)的效率也無法與現(xiàn)代桌面芯片的內(nèi)置正弦函數(shù)相比。要準(zhǔn)確快速地計(jì)算正弦函數(shù)和其他函數(shù),需要非常仔細(xì)的算法,專門用于避

免無意地將小的誤差變成大的錯(cuò)誤。這些算法一般內(nèi)置在硬件中以更快地執(zhí)行。例如,幾乎每個(gè)在最近 10 年內(nèi)組裝的 X86

芯片都具有正弦和余弦函的硬件實(shí)現(xiàn),X86 VM 只需調(diào)用即可,不用基于較原始的運(yùn)算緩慢地計(jì)算它們。HotSpot

利用這些指令顯著加速了三角函數(shù)的運(yùn)算。

每個(gè)高中學(xué)生都學(xué)過勾股定理:在直角三角形中,斜邊邊長的平方等于兩條直角邊邊長平方之和。即 c

2 =a

2 + b

2

學(xué)習(xí)過大學(xué)物理和高等數(shù)學(xué)的同學(xué)會(huì)發(fā)現(xiàn),這個(gè)等式會(huì)在很多地方出現(xiàn),不只是在直角三角形中。例如,R

2 的平方、二維向量的長度、三角不等式等都存在勾股定理。(事實(shí)上,這些只是看待同一件事情的不同方式。重點(diǎn)在于勾股定理比看上去要重要得多)。

Java 5 添加了 Math.hypot 函數(shù)來精確執(zhí)行這種計(jì)算,這也是庫很有用的一個(gè)出色的實(shí)例證明。原始的簡單方法如下:

public static double hypot(double x, double y){

return x*x + y*y;

}

實(shí)際代碼更復(fù)雜一些,如清單 2 所示。首先應(yīng)注意的一點(diǎn)是,這是以本機(jī) C 代碼編寫的,以使性能最大化。要注意的第二點(diǎn)是,它盡力使本計(jì)算中出現(xiàn)的錯(cuò)誤最少。事實(shí)上,應(yīng)根據(jù) x 和 y 的相對(duì)大小選擇不同的算法。

清單 2. 實(shí)現(xiàn) Math.hypot

的實(shí)際代碼/*

* ====================================================

* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.

*

* Developed at SunSoft, a Sun Microsystems, Inc. business.

* Permission to use, copy, modify, and distribute this

* software is freely granted, provided that this notice

* is preserved.

* ====================================================

*/

#include "fdlibm.h"

#ifdef __STDC__

double __ieee754_hypot(double x, double y)

#else

double __ieee754_hypot(x,y)

double x, y;

#endif

{

double a=x,b=y,t1,t2,y1,y2,w;

int j,k,ha,hb;

ha = __HI(x)&0x7fffffff; /* high word of x */

hb = __HI(y)&0x7fffffff; /* high word of y */

if(hb > ha) {a=y;b=x;j=ha; ha=hb;hb=j;} else {a=x;b=y;}

__HI(a) = ha; /* a

__HI(b) = hb; /* b

if((ha-hb)>0x3c00000) {return a+b;} /* x/y > 2**60 */

k=0;

if(ha > 0x5f300000) { /* a>2**500 */

if(ha >= 0x7ff00000) { /* Inf or NaN */

w = a+b; /* for sNaN */

if(((ha&0xfffff)|__LO(a))==0) w = a;

if(((hb^0x7ff00000)|__LO(b))==0) w = b;

return w;

}

/* scale a and b by 2**-600 */

ha -= 0x25800000; hb -= 0x25800000; k += 600;

__HI(a) = ha;

__HI(b) = hb;

}

if(hb < 0x20b00000) { /* b < 2**-500 */

if(hb <= 0x000fffff) { /* subnormal b or 0 */

if((hb|(__LO(b)))==0) return a;

t1=0;

__HI(t1) = 0x7fd00000; /* t1=2^1022 */

b *= t1;

a *= t1;

k -= 1022;

} else { /* scale a and b by 2^600 */

ha += 0x25800000; /* a *= 2^600 */

hb += 0x25800000; /* b *= 2^600 */

k -= 600;

__HI(a) = ha;

__HI(b) = hb;

}

}

/* medium size a and b */

w = a-b;

if (w>b) {

t1 = 0;

__HI(t1) = ha;

t2 = a-t1;

w = sqrt(t1*t1-(b*(-b)-t2*(a+t1)));

} else {

a = a+a;

y1 = 0;

__HI(y1) = hb;

y2 = b - y1;

t1 = 0;

__HI(t1) = ha+0x00100000;

t2 = a - t1;

w = sqrt(t1*y1-(w*(-w)-(t1*y2+t2*b)));

}

if(k!=0) {

t1 = 1.0;

__HI(t1) += (k<<20);

return t1*w;

} else return w;

}

實(shí)際上,是使用這種特定函數(shù),還是幾個(gè)其他類似函數(shù)中的一個(gè)取決于平臺(tái)上的 JVM 細(xì)節(jié)。不過,這種代碼很有可能在 Sun 的標(biāo)準(zhǔn) JDK 中調(diào)用。(其他 JDK 實(shí)現(xiàn)可以在必要時(shí)改進(jìn)它。)

這段代碼(以及 Sun Java 開發(fā)庫中的大多數(shù)其他本機(jī)數(shù)學(xué)代碼)來自 Sun 約 15 年前編寫的開源 fdlibm 庫。該庫用于精確實(shí)現(xiàn) IEE754 浮點(diǎn)數(shù),能進(jìn)行非常準(zhǔn)確的計(jì)算,不過會(huì)犧牲一些性能。

對(duì)數(shù)說明一個(gè)底數(shù)的幾次冪等于一個(gè)給定的值。也就是說,它是 Math.pow() 函數(shù)的反函數(shù)。以 10 為底的對(duì)數(shù)一般出現(xiàn)在工程應(yīng)用程序中。以 e為底的對(duì)數(shù)(自然對(duì)數(shù))出現(xiàn)在復(fù)合計(jì)算以及大量科學(xué)和數(shù)學(xué)應(yīng)用程序中。以 2 為底的對(duì)數(shù)一般出現(xiàn)在算法分析中。

從 Java 1.0 開始,Math 類有了一個(gè)自然對(duì)數(shù)。也就是給定一個(gè)參數(shù) x,該自然對(duì)數(shù)返回 e 的幾次冪等于給定的值 x。遺憾的是,Java 語言的(以及 C 、Fortran 和 Basic 的)自然對(duì)數(shù)函數(shù)錯(cuò)誤命名為 log()。在我讀的每本數(shù)學(xué)教材中,log 都是以 10 為底的對(duì)數(shù),而 ln 是以 e 為底的對(duì)數(shù),lg 是以 2 為底的對(duì)數(shù)。現(xiàn)在已經(jīng)來不及修復(fù)這個(gè)問題了,不過 Java 5 添加了一個(gè) log10() 函數(shù),它是以 10 為底而不是以 e 為底的對(duì)數(shù)。

清單 3 是一個(gè)簡單程序,它輸出整數(shù) 1 到 100 的以 2、10 和 e 為底的對(duì)數(shù):

清單 3. 1 到 100 的各種底數(shù)的對(duì)數(shù)

public class Logarithms {

public static void main(String[] args) {

for (int i = 1; i <= 100; i++) {

System.out.println(i + "/t" +

Math.log10(i) + "/t" +

Math.log(i) + "/t" +

lg(i));

}

}

public static double lg(double x) {

return Math.log(x)/Math.log(2.0);

}

}

下面是前 10 行結(jié)果:

1 0.0 0.0 0.0

2 0.3010299956639812 0.6931471805599453 1.0

3 0.47712125471966244 1.0986122886681096 1.584962500721156

4 0.6020599913279624 1.3862943611198906 2.0

5 0.6989700043360189 1.6094379124341003 2.321928094887362

6 0.7781512503836436 1.791759469228055 2.584962500721156

7 0.8450980400142568 1.9459101490553132 2.807354922057604

8 0.9030899869919435 2.0794415416798357 3.0

9 0.9542425094393249 2.1972245773362196 3.1699250014423126

10 1.0 2.302585092994046 3.3219280948873626

Math.log10() 能正常終止對(duì)數(shù)函數(shù)執(zhí)行:0 或任何負(fù)數(shù)的對(duì)數(shù)返回 NaN。

我不敢說我的生活中曾經(jīng)需要過立方根,我也不是每天都要使用代數(shù)和幾何的少數(shù)人士之一,更別提偶然涉足微積分、微分方程,甚至抽象代數(shù)。因此,下面這個(gè)函數(shù)對(duì)我毫無用處。盡管如此,如果意外需要計(jì)算立方根,現(xiàn)在就可以了 — 使用自 Java 5 開始引入的 Math.cbrt() 方法。清單 4 通過計(jì)算 -5 到 5 之間的整數(shù)的立方根進(jìn)行了演示:

清單 4. -5 到 5 的立方根

public class CubeRoots {

public static void main(String[] args) {

for (int i = -5; i <= 5; i++) {

System.out.println(Math.cbrt(i));

}

}

}

下面是結(jié)果:

-1.709975946676697

-1.5874010519681996

-1.4422495703074083

-1.2599210498948732

-1.0

0.0

1.0

1.2599210498948732

1.4422495703074083

1.5874010519681996

1.709975946676697

結(jié)果顯示,與平方根相比,立方根擁有一個(gè)不錯(cuò)的特性:每個(gè)實(shí)數(shù)只有一個(gè)實(shí)立方根。這個(gè)函數(shù)只在其參數(shù)為 NaN 時(shí)才返回 NaN。

雙曲三角函數(shù)就是對(duì)曲線應(yīng)用三角函數(shù),也就是說,想象將這些點(diǎn)放在笛卡爾平面上來得到 t 的所有可能值:

x = r cos(t)

y = r sin(t)

您會(huì)得到以 r 為半徑的曲線。相反,假設(shè)改用雙曲正弦和雙曲余弦,如下所示:

x = r cosh(t)

y = r sinh(t)

則會(huì)得到一個(gè)正交雙曲線,原點(diǎn)與它最接近的點(diǎn)之間的距離是 r。

還可以這樣思考:其中 sin(x) 可以寫成 (ei

x - e-i

x)/2,cos(x) 可以寫成 (ei

x + e-i

x)/2,從這些公式中刪除虛數(shù)單位后即可得到雙曲正弦和雙曲余弦,即 sinh(x) = (e

x - e

-x)/2,cosh(x) = (e

x + e

-x)/2。

Java 5 添加了所有這三個(gè)函數(shù):Math.cosh()、Math.sinh() 和 Math.tanh()。還沒有包含反雙曲三角函數(shù) — 反雙曲余弦、反雙曲正弦和反雙曲正切。

實(shí)際上,cosh(z) 的結(jié)果相當(dāng)于一根吊繩兩端相連后得到的形狀,即懸鏈線。清單 5 是一個(gè)簡單的程序,它使用 Math.cosh 函數(shù)繪制一條懸鏈線:

清單 5. 使用 Math.cosh() 繪制懸鏈線

import java.awt.*;

public class Catenary extends Frame {

private static final int WIDTH = 200;

private static final int HEIGHT = 200;

private static final double MIN_X = -3.0;

private static final double MAX_X = 3.0;

private static final double MAX_Y = 8.0;

private Polygon catenary = new Polygon();

public Catenary(String title) {

super(title);

setSize(WIDTH, HEIGHT);

for (double x = MIN_X; x <= MAX_X; x += 0.1) {

double y = Math.cosh(x);

int scaledX = (int) (x * WIDTH/(MAX_X - MIN_X) + WIDTH/2.0);

int scaledY = (int) (y * HEIGHT/MAX_Y);

// in computer graphics, y extends down rather than up as in

// Caretesian coordinates' so we have to flip

scaledY = HEIGHT - scaledY;

catenary.addPoint(scaledX, scaledY);

}

}

public static void main(String[] args) {

Frame f = new Catenary("Catenary");

f.setVisible(true);

}

public void paint(Graphics g) {

g.drawPolygon(catenary);

}

}

圖 1 為繪制的曲線:

圖 1. 笛卡爾平面中的一條懸鏈曲線

雙曲正弦、雙曲余弦和雙曲正切函數(shù)也會(huì)以常見或特殊形式出現(xiàn)在各種計(jì)算中。

Math.signum 函數(shù)將正數(shù)轉(zhuǎn)換為 1.0,將負(fù)數(shù)轉(zhuǎn)換為 -1.0,0 仍然是 0。 實(shí)際上,它只是提取一個(gè)數(shù)的符號(hào)。在實(shí)現(xiàn) Comparable 接口時(shí),這很有用。

一個(gè) float 和一個(gè) double 版本可用來維護(hù)這種類型 。這個(gè)函數(shù)的用途很明顯,即處理浮點(diǎn)運(yùn)算、NaN 以及正 0 和負(fù) 0 的特殊情況。NaN 也被當(dāng)作 0,正 0 和負(fù) 0 應(yīng)該返回正 0 和 負(fù) 0。例如,假設(shè)如清單 6 那樣用簡單的原始方法實(shí)現(xiàn)這個(gè)函數(shù):

清單 6. 存在問題的 Math.signum 實(shí)現(xiàn)

public static double signum(double x) {

if (x == 0.0) return 0;

else if (x < 0.0) return -1.0;

else return 1.0;

}

首先,這個(gè)方法會(huì)將所有負(fù) 0 轉(zhuǎn)換為正 0。(負(fù) 0 可能不好理解,但它確實(shí)是 IEEE 754 規(guī)范的必要組成部分)。其次,它會(huì)認(rèn)為 NaN 是正的。實(shí)際實(shí)現(xiàn)如清單 7 所示,它更加復(fù)雜,而且會(huì)仔細(xì)處理這些特殊情況:

清單 7. 實(shí)際的、正確的 Math.signum 實(shí)現(xiàn)

public static double signum(double d) {

return (d == 0.0 || isNaN(d))?d:copySign(1.0, d);

}

public static double copySign(double magnitude, double sign) {

return rawCopySign(magnitude, (isNaN(sign)?1.0d:sign));

}

public static double rawCopySign(double magnitude, double sign) {

return Double.longBitsToDouble((Double.doubleToRawLongBits(sign) &

(DoubleConsts.SIGN_BIT_MASK)) |

(Double.doubleToRawLongBits(magnitude) &

(DoubleConsts.EXP_BIT_MASK |

DoubleConsts.SIGNIF_BIT_MASK)));

}

最有效的代碼是從您未編寫過的代碼。不要做專家們已經(jīng)做過的事情。使用 java.lang.Math 函數(shù)(新的和舊的)的代碼將更快、更有效,而且比您自己編寫的任何代碼都準(zhǔn)確。所以請(qǐng)使用這些函數(shù)。

學(xué)習(xí)

類型、值和變量:Java 語言規(guī)范的第 4 章討論了浮點(diǎn)運(yùn)算。

二進(jìn)制浮點(diǎn)運(yùn)算的 IEEE 標(biāo)準(zhǔn):IEEE 754 標(biāo)準(zhǔn)定義了大多數(shù)現(xiàn)代處理器和語言(包括 Java 語言)中的浮點(diǎn)運(yùn)算。

java.lang.Math:提供本文所討論函數(shù)的類的 Javadoc。

Bug 5005861:不滿足的用戶要求 JDK 中包含更快的三角函數(shù)。

懸鏈線:Wikipedia 說明了懸鏈線的歷史及其背后的數(shù)學(xué)理論。

獲得產(chǎn)品和技術(shù)

fdlibm:一個(gè)適用于支持 IEEE 754 浮點(diǎn)數(shù)的機(jī)器的 C math 庫,可在 Netlib 數(shù)學(xué)軟件庫中找到。

OpenJDK:查看此開源 Java SE 實(shí)現(xiàn)中 math 類的源代碼。

Elliotte Rusty Harold 出生在新奧爾良,現(xiàn)在他還定期回老家喝一碗秋葵湯。他與他的妻子 Beth、寵物貓 Charm(以 quark 命名)和 Marjorie(以他岳母的名字命名)住在 Irvine 附近的大學(xué)城中心。他的 Cafe au Lait Web 站點(diǎn)已成為 Internet 上最流行的獨(dú)立 Java 站點(diǎn)之一,而且其姊妹站點(diǎn) Cafe con Leche 已經(jīng)是最流行的 XML 站點(diǎn)之一。他最近的著作是 Refactoring HTML。

總結(jié)

以上是生活随笔為你收集整理的java中怎样定义实数_Java Math 类中的新功能,第 1 部分: 实数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。