知识大全 Java Math类中的新功能,第1 部分:实数
Posted 函数
篇首语:先学爬,然后学走。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 Java Math类中的新功能,第1 部分:实数相关的知识,希望对你有一定的参考价值。
Java Math类中的新功能,第1 部分:实数 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
摘要 在这篇由两部分组成的文章中 Elliotte Rusty Harold 与您一起探讨经典 java lang Math 类中的 新 功能 第 部分主要讨论比较单调的数学函数 第 部分将探讨专为操作浮点数而设计的函数
有时候您会对一个类熟悉到忘记了它的存在 如果您能够写出 java lang Foo 的文档 那么 Eclipse 将帮助您自动完成所需的函数 您无需阅读它的 Javadoc 例如 我使用 java lang Math(一个我自认为非常了解的类)时就是这样 但令我吃惊的是 我最近偶然读到它的 Javadoc —— 这可能是我近五年来第一次读到 我发现这个类的大小几乎翻了一倍 包含 种我从来没听说过的新方法 看来我要对它另眼相看了
Java&# ;语言规范第 版向 java lang Math(以及它的姊妹版 java lang StrictMath)添加了 种新方法 Java 又添加了 种 在本文中 我重点讨论其中的比较单调的数学函数 如 log 和 cosh 在第 部分 我将探讨专为操作浮点数(与抽象实数相反)而设计的函数
抽象实数(如 π 或 )与 Java double 之间的区别很明显 首先 数的理想状态是具有无限的精度 而 Java 表示法把数限制为固定位数 在处理非常大和非常小的数时 这点很重要 例如 (二十亿零一)可以精确表示为一个 int 而不是一个 float 最接近的浮点数表示形式是 E — 即两亿 使用 double 数会更好 因为它们的位数更多(这是应该总是使用 double 数而不是 float 数的理由之一) 但它们的精度仍然受到一定限制
计算机算法(Java 语言和其他语言的算法)的第二个限制是它基于二进制而不是十进制 / 和 / 之类的分数可用十进制精确表示(分别是 和 ) 但用二进制表示时 就会出现重复的分数 如同 / 在用十进制表示时 就会变为 ……以 为基数 任何分母仅包含质数因子 和 的分数都可以精确表示 以 为基数 则只有分母是 的乘方的分数才可以精确表示 / / / / 等
这种不精确性是迫切需要一个 math 类的最主要的原因之一 当然 您可以只使用标准的 + 和 * 运算符以及一个简单的循环来定义三角函数和其他使用泰勒级数展开式的函数 如清单 所示
清单 使用泰勒级数计算正弦
public class SineTaylor public static void main(String[] args) for (double angle = ; angle <= *Math PI; angle += Math PI/ ) System out println(degrees(angle) + \\t + taylorSeriesSine(angle) + \\t + Math sin(angle)); public static double degrees(double radians) return * radians/ Math PI; public static double taylorSeriesSine(double radians) double sine = ; int sign = ; for (int i = ; i < ; i+= ) sine += Math pow(radians i) * sign / factorial(i); sign *= ; return sine; private static double factorial(int i) double result = ; for (int j = ; j <= i; j++) result *= j; return result;
开始运行得不错 只有一点小的误差 如果存在误差的话 也只是最后一位小数不同
但是 随着角度的增加 误差开始变大 这种简单的方法就不是很适用了
这里使用泰勒级数得到的结果实际上比我想像的要精确 但是 随着角度增加到 度 度( pi 弧度)以及更大时 泰勒级数就逐渐需要更多条件来进行准确计算 java lang Math 使用的更加完善的算法就避免了这一点
泰勒级数的效率也无法与现代桌面芯片的内置正弦函数相比 要准确快速地计算正弦函数和其他函数 需要非常仔细的算法 专门用于避免无意地将小的误差变成大的错误 这些算法一般内置在硬件中以更快地执行 例如 几乎每个在最近 年内组装的 X 芯片都具有正弦和余弦函的硬件实现 X VM 只需调用即可 不用基于较原始的运算缓慢地计算它们 HotSpot 利用这些指令显著加速了三角函数的运算
直角三角形和欧几里德范数
每个高中学生都学过勾股定理 在直角三角形中 斜边边长的平方等于两条直角边边长平方之和 即 c = a + b
学习过大学物理和高等数学的同学会发现 这个等式会在很多地方出现 不只是在直角三角形中 例如 R 的平方 二维向量的长度 三角不等式等都存在勾股定理 (事实上 这些只是看待同一件事情的不同方式 重点在于勾股定理比看上去要重要得多)
Java 添加了 Math hypot 函数来精确执行这种计算 这也是库很有用的一个出色的实例证明 原始的简单方法如下
public static double hypot(double x double y) return x*x + y*y;
实际代码更复杂一些 如清单 所示 首先应注意的一点是 这是以本机 C 代码编写的 以使性能最大化 要注意的第二点是 它尽力使本计算中出现的错误最少 事实上 应根据 x 和 y 的相对大小选择不同的算法
清单 实现 Math hypot
的实际代码/** ====================================================* Copyright (C) by Sun Microsystems Inc All rights reserved ** Developed at SunSoft a Sun Microsystems Inc business * Permission to use copy modify and distribute this* sofare is freely granted provided that this notice * is preserved * ====================================================*/#include fdlibm h #ifdef __STDC__ double __ieee _hypot(double x double y)#else double __ieee _hypot(x y) double x y;#endif double a=x b=y t t y y w; int j k ha hb; ha = __HI(x)& x fffffff; /* high word of x */ hb = __HI(y)& x fffffff; /* 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 < |a| */ __HI(b) = hb; /* b < |b| */ if((ha hb)> x c ) return a+b; /* x/y > ** */ k= ; if(ha > x f ) /* a> ** */ if(ha >= x ff ) /* Inf or NaN */ w = a+b; /* for sNaN */ if(((ha& xfffff)|__LO(a))== ) w = a; if(((hb^ x ff )|__LO(b))== ) w = b; return w; /* scale a and b by ** */ ha = x ; hb = x ; k += ; __HI(a) = ha; __HI(b) = hb; if(hb < x b ) /* b < ** */ if(hb <= x fffff) /* subnormal b or */ if((hb|(__LO(b)))== ) return a; t = ; __HI(t ) = x fd ; /* t = ^ */ b *= t ; a *= t ; k = ; else /* scale a and b by ^ */ ha += x ; /* a *= ^ */ hb += x ; /* b *= ^ */ k = ; __HI(a) = ha; __HI(b) = hb; /* medium size a and b */ w = a b; if (w>b) t = ; __HI(t ) = ha; t = a t ; w = sqrt(t *t (b*( b) t *(a+t ))); else a = a+a; y = ; __HI(y ) = hb; y = b y ; t = ; __HI(t ) = ha+ x ; t = a t ; w = sqrt(t *y (w*( w) (t *y +t *b))); if(k!= ) t = ; __HI(t ) += (k<< ); return t *w; else return w;
实际上 是使用这种特定函数 还是几个其他类似函数中的一个取决于平台上的 JVM 细节 不过 这种代码很有可能在 Sun 的标准 JDK 中调用 (其他 JDK 实现可以在必要时改进它 )
这段代码(以及 Sun Java 开发库中的大多数其他本机数学代码)来自 Sun 约 年前编写的开源 fdlibm 库 该库用于精确实现 IEE 浮点数 能进行非常准确的计算 不过会牺牲一些性能
以 为底的对数
对数说明一个底数的几次幂等于一个给定的值 也就是说 它是 Math pow() 函数的反函数 以 为底的对数一般出现在工程应用程序中 以 e为底的对数(自然对数)出现在复合计算以及大量科学和数学应用程序中 以 为底的对数一般出现在算法分析中
从 Java 开始 Math 类有了一个自然对数 也就是给定一个参数 x 该自然对数返回 e 的几次幂等于给定的值 x 遗憾的是 Java 语言的(以及 C Fortran 和 Basic 的)自然对数函数错误命名为 log() 在我读的每本数学教材中 log 都是以 为底的对数 而 ln 是以 e 为底的对数 lg 是以 为底的对数 现在已经来不及修复这个问题了 不过 Java 添加了一个 log () 函数 它是以 为底而不是以 e 为底的对数
清单 是一个简单程序 它输出整数 到 的以 和 e 为底的对数
清单 到 的各种底数的对数
public static void main(String[] args) for (int i = ; i <= ; i++) System out println(i + \\t + Math log (i) + \\t + Math log(i) + \\t + lg(i));
public static double lg(double x) return Math log(x)/Math log( );
下面是前 行结果
Math log () 能正常终止对数函数执行 或任何负数的对数返回 NaN
立方根
我不敢说我的生活中曾经需要过立方根 我也不是每天都要使用代数和几何的少数人士之一 更别提偶然涉足微积分 微分方程 甚至抽象代数 因此 下面这个函数对我毫无用处 尽管如此 如果意外需要计算立方根 现在就可以了 — 使用自 Java 开始引入的 Math cbrt() 方法 清单 通过计算 到 之间的整数的立方根进行了演示
清单 到 的立方根
public class CubeRoots public static void main(String[] args) for (int i = ; i <= ; i++) System out println(Math cbrt(i));
下面是结果
结果显示 与平方根相比 立方根拥有一个不错的特性 每个实数只有一个实立方根 这个函数只在其参数为 NaN 时才返回 NaN
双曲三角函数
双曲三角函数就是对曲线应用三角函数 也就是说 想象将这些点放在笛卡尔平面上来得到 t 的所有可能值
x = r cos(t)y = r sin(t)
您会得到以 r 为半径的曲线 相反 假设改用双曲正弦和双曲余弦 如下所示
x = r cosh(t)y = r sinh(t)
则会得到一个正交双曲线 原点与它最接近的点之间的距离是 r
还可以这样思考 其中 sin(x) 可以写成 (ei x e i x)/ cos(x) 可以写成 (ei x + e i x)/ 从这些公式中删除虚数单位后即可得到双曲正弦和双曲余弦 即 sinh(x) = (e x e x)/ cosh(x) = (e x + e x)/
Java 添加了所有这三个函数 sh() Math sinh() 和 Math tanh() 还没有包含反双曲三角函数 — 反双曲余弦 反双曲正弦和反双曲正切
实际上 cosh(z) 的结果相当于一根吊绳两端相连后得到的形状 即悬链线 清单 是一个简单的程序 它使用 sh 函数绘制一条悬链线
清单 使用 sh() 绘制悬链线
import java awt *;
public class Catenary extends Frame
private static final int WIDTH = ; private static final int HEIGHT = ; private static final double MIN_X = ; private static final double MAX_X = ; private static final double MAX_Y = ;
private Polygon catenary = new Polygon();
public Catenary(String title) super(title); setSize(WIDTH HEIGHT); for (double x = MIN_X; x <= MAX_X; x += ) double y = sh(x); int scaledX = (int) (x * WIDTH/(MAX_X MIN_X) + WIDTH/ ); int scaledY = (int) (y * HEIGHT/MAX_Y); // in puter 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);
图 为绘制的曲线
图 笛卡尔平面中的一条悬链曲线
双曲正弦 双曲余弦和双曲正切函数也会以常见或特殊形式出现在各种计算中
符号
Math signum 函数将正数转换为 将负数转换为 仍然是 实际上 它只是提取一个数的符号 在实现 Comparable 接口时 这很有用
一个 float 和一个 double 版本可用来维护这种类型 这个函数的用途很明显 即处理浮点运算 NaN 以及正 和负 的特殊情况 NaN 也被当作 正 和负 应该返回正 和 负 例如 假设如清单 那样用简单的原始方法实现这个函数
清单 存在问题的 Math signum 实现
public static double signum(double x) if (x == ) return ; else if (x < ) return ; else return ;
首先 这个方法会将所有负 转换为正 (负 可能不好理解 但它确实是 IEEE 规范的必要组成部分) 其次 它会认为 NaN 是正的 实际实现如清单 所示 它更加复杂 而且会仔细处理这些特殊情况
清单 实际的 正确的 Math signum 实现
public static double signum(double d) return (d == || isNaN(d))?d:copySign( d);
public static double copySign(double magnitude double sign) return rawCopySign(magnitude (isNaN(sign)? d: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)));
事半功倍
最有效的代码是从您未编写过的代码 不要做专家们已经做过的事情 使用 java lang Math 函数(新的和旧的)的代码将更快 更有效 而且比您自己编写的任何代码都准确 所以请使用这些函数
参考资料
您可以参阅本文在 developerWorks 全球网站上的 英文原文
类型 值和变量 Java 语言规范的第 章讨论了浮点运算
二进制浮点运算的 IEEE 标准 IEEE 标准定义了大多数现代处理器和语言(包括 Java 语言)中的浮点运算
java lang Math
提供本文所讨论函数的类的 Javadoc
Bug 不满足的用户要求 JDK 中包含更快的三角函数
关于作者
cha138/Article/program/Java/hx/201311/26110相关参考
知识大全 Java ME中的Math.pow()方法使用详解
JavaME中的Math.pow()方法使用详解 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
Java类中类属性和对象属性的初始化顺序 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! Java
Java中的枚举器(反复器) 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 在任何集合类中必须通
轻松掌握Java泛型(第1部分) 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! JSE-代号为T
知识大全 Java运行时监控,第1部分: Java运行时性能和可用性监控
Java运行时监控,第1部分:Java运行时性能和可用性监控 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看
知识大全 深入Java调试体系: 第1部分,JPDA体系概览
深入Java调试体系:第1部分,JPDA体系概览 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
安全技术Java与安全性,第1部分一 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! WebLog
安全技术Java与安全性,第1部分二(图) 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 用户和
知识大全 面向Java开发人员的Scala指南: 构建计算器,第1 部分
面向Java开发人员的Scala指南:构建计算器,第1部分 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一
安全技术Java与安全性,第2部分二 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! Privil