Java 数学工具类 Math
目录
一、核心字段:自然常数与圆周率
1. Math.E
2. Math.PI
二、基础运算方法
1.绝对值计算:abs 系列方法
2.加减乘除与精确运算
1. 加法:addExact
2. 减法:subtractExact
3. 乘法
4.取整与舍入
1. ceil(double a):向上取整
2. floor(double a):向下取整
3. rint(double a):四舍五入到最近整数(返回 double)
4. round:四舍五入到整数(返回 int 或 long)
3.三角函数与双曲线函数
1.基本三角函数:sin、cos、tan
2.角度与弧度转换
3.反三角函数:asin、acos、atan、atan2
4.双曲线函数:sinh、cosh、tanh
4.指数与对数运算
1.指数运算:exp、expm1、pow
1.1. exp(double a):计算自然指数 e^a
1.2. expm1(double x):计算 e^x - 1
1.3. pow(double a, double b):计算 a^b
2.对数运算:log、log10、log1p
2.1、log(double a):计算自然对数 ln(a)
2.2、log10(double a):计算以 10 为底的对数 log10(a)
2.3、log1p(double x):计算 ln(1 + x)
三、其他实用方法
1、平方根与立方根:sqrt、cbrt
2、随机数生成:random
3、符号与 ulp 操作
1. 衡量浮点数的 “接近程度”(替代直接相等判断)
2. 分析数值算法的精度
3. 调试浮点计算异常
四、资料来源
在 Java 编程中,数值计算是一项基础且核心的任务。无论是简单的加减乘除,还是复杂的三角函数、指数运算,Java 标准库都为我们提供了一个强大的工具 ——java.lang.Math
类。这个类包含了大量用于执行基本数值运算的静态方法,涵盖了从简单的绝对值计算到复杂的浮点运算等多个方面。
一、核心字段:自然常数与圆周率
Math 类定义了两个常用的数学常数,它们是程序中进行科学计算的基础:
1. Math.E
表示自然对数的底数 e
(约等于 2.71828),是一个 double
类型的常量。
public class MathConstants {public static void main(String[] args) {System.out.println("圆周率 π: " + Math.PI); // 输出:3.141592653589793// 示例:计算半径为 5 的圆的面积double radius = 5;double area = Math.PI * radius * radius;System.out.println("圆的面积: " + area); // 输出:78.53981633974483}
}
2. Math.PI
表示圆周率 π
(约等于 3.14159),同样是 double
类型的常量,常用于几何计算。
public class MathConstants {public static void main(String[] args) {System.out.println("圆周率 π: " + Math.PI); // 输出:3.141592653589793// 示例:计算半径为 5 的圆的面积double radius = 5;double area = Math.PI * radius * radius;System.out.println("圆的面积: " + area); // 输出:78.53981633974483}
}
二、基础运算方法
1.绝对值计算:abs
系列方法
abs
方法用于计算各种数值类型的绝对值,支持 int
、long
、float
、double
四种类型:
方法签名 功能描述 static int abs(int a)
返回 int 类型的绝对值 static long abs(long a)
返回 long 类型的绝对值 static float abs(float a)
返回 float 类型的绝对值 static double abs(double a)
返回 double 类型的绝对值
示例代码:
public class MathAbsExample {public static void main(String[] args) {int intNum = -10;long longNum = -10000000000L;float floatNum = -3.14f;double doubleNum = -2.71828;System.out.println("int 绝对值: " + Math.abs(intNum)); // 输出:10System.out.println("long 绝对值: " + Math.abs(longNum)); // 输出:10000000000System.out.println("float 绝对值: " + Math.abs(floatNum)); // 输出:3.14System.out.println("double 绝对值: " + Math.abs(doubleNum)); // 输出:2.71828}
}
注意:对于 int
类型,Integer.MIN_VALUE
(-2147483648)的绝对值是无法用 int
表示的(因为最大值为 2147483647),此时 Math.abs(Integer.MIN_VALUE)
会返回其本身(仍为负数)。若需检测这种溢出情况,可使用 absExact
方法:
public class MathAbsExactExample {public static void main(String[] args) {try {int minInt = Integer.MIN_VALUE;System.out.println(Math.absExact(minInt)); // 抛出 ArithmeticException} catch (ArithmeticException e) {System.out.println("错误:绝对值溢出!" + e.getMessage());}}
}
2.加减乘除与精确运算
Math 类提供了普通的算术运算支持,同时为了应对溢出问题,引入了一系列带「Exact」后缀的方法,当运算结果溢出时会抛出 ArithmeticException
。
1. 加法:addExact
public class MathAddExactExample {public static void main(String[] args) {// 正常情况int a = 1000000;int b = 2000000;System.out.println(Math.addExact(a, b)); // 输出:3000000// 溢出情况try {int maxInt = Integer.MAX_VALUE;System.out.println(Math.addExact(maxInt, 1)); // 抛出异常} catch (ArithmeticException e) {System.out.println("加法溢出:" + e.getMessage());}}
}
2. 减法:subtractExact
public class MathSubtractExactExample {public static void main(String[] args) {long x = 5000000000L;long y = 3000000000L;System.out.println(Math.subtractExact(x, y)); // 输出:2000000000try {long minLong = Long.MIN_VALUE;System.out.println(Math.subtractExact(minLong, 1)); // 抛出异常} catch (ArithmeticException e) {System.out.println("减法溢出:" + e.getMessage());}}
}
3. 乘法
multiplyExact
:返回乘积,溢出时抛出异常。multiplyFull
:返回两个 int 的精确乘积(结果为 long,避免溢出)。
public class MathMultiplyExample {public static void main(String[] args) {// multiplyExactint m = 123456;int n = 789012;try {System.out.println(Math.multiplyExact(m, n)); // 结果可能溢出 int} catch (ArithmeticException e) {System.out.println("乘法溢出:" + e.getMessage());}// multiplyFull:安全计算 int 乘积(返回 long)long product = Math.multiplyFull(m, n);System.out.println("精确乘积(long):" + product); // 输出:97407519744}
}
4.取整与舍入
在处理浮点数时,我们经常需要将其转换为整数,Math 类提供了多种取整方法,适用于不同场景:
1. ceil(double a)
:向上取整
返回大于或等于参数的最小整数(以 double 形式表示)。
System.out.println(Math.ceil(3.2)); // 输出:4.0
System.out.println(Math.ceil(-3.2)); // 输出:-3.0
System.out.println(Math.ceil(5.0)); // 输出:5.0
2. floor(double a)
:向下取整
返回小于或等于参数的最大整数(以 double 形式表示)。
System.out.println(Math.floor(3.8)); // 输出:3.0
System.out.println(Math.floor(-3.8)); // 输出:-4.0
System.out.println(Math.floor(5.0)); // 输出:5.0
3. rint(double a)
:四舍五入到最近整数(返回 double)
若参数距离两个整数等距,则返回偶数。
System.out.println(Math.rint(3.2)); // 输出:3.0
System.out.println(Math.rint(3.5)); // 输出:4.0(与 3.5 等距,取偶数)
System.out.println(Math.rint(4.5)); // 输出:4.0(与 4.5 等距,取偶数)
4. round
:四舍五入到整数(返回 int 或 long)
round(float a)
:返回 intround(double a)
:返回 long
System.out.println(Math.round(3.2f)); // 输出:3(int)
System.out.println(Math.round(3.8f)); // 输出:4(int)
System.out.println(Math.round(3.5)); // 输出:4(long)
System.out.println(Math.round(-3.5)); // 输出:-3(long,向正无穷大舍入)
3.三角函数与双曲线函数
Math 类提供了完整的三角函数支持,包括正弦、余弦、正切及其反函数,所有角度参数均以弧度为单位。
1.基本三角函数:sin
、cos
、tan
public class TrigonometryExample {public static void main(String[] args) {double angle = Math.PI / 6; // 30 度(π/6 弧度)// 正弦double sinVal = Math.sin(angle);System.out.println("sin(30°) = " + sinVal); // 输出:0.5(近似)// 余弦double cosVal = Math.cos(angle);System.out.println("cos(30°) = " + cosVal); // 输出:0.8660254...(√3/2 近似)// 正切double tanVal = Math.tan(angle);System.out.println("tan(30°) = " + tanVal); // 输出:0.57735...(1/√3 近似)}
}
2.角度与弧度转换
由于三角函数参数为弧度,可使用 toRadians
(度转弧度)和 toDegrees
(弧度转度)进行转换:
double degrees = 45;
double radians = Math.toRadians(degrees);
System.out.println(degrees + "度 = " + radians + "弧度"); // 输出:45.0度 = 0.785398...弧度double rad = Math.PI / 2;
double deg = Math.toDegrees(rad);
System.out.println(rad + "弧度 = " + deg + "度"); // 输出:1.570796...弧度 = 90.0度
3.反三角函数:asin
、acos
、atan
、atan2
public class InverseTrigExample {public static void main(String[] args) {// 反正弦(返回 [-π/2, π/2] 弧度)double asin = Math.asin(0.5);System.out.println("arcsin(0.5) = " + Math.toDegrees(asin) + "度"); // 输出:30.0度// 反余弦(返回 [0, π] 弧度)double acos = Math.acos(0.5);System.out.println("arccos(0.5) = " + Math.toDegrees(acos) + "度"); // 输出:60.0度// 反正切(返回 [-π/2, π/2] 弧度)double atan = Math.atan(1);System.out.println("arctan(1) = " + Math.toDegrees(atan) + "度"); // 输出:45.0度// 坐标反正切(根据 (x,y) 计算角度,返回 [-π, π] 弧度)double x = 1;double y = 1;double atan2 = Math.atan2(y, x);System.out.println("atan2(" + y + "," + x + ") = " + Math.toDegrees(atan2) + "度"); // 输出:45.0度}
}
4.双曲线函数:sinh
、cosh
、tanh
双曲线函数是三角函数的类比,基于指数函数定义:
public class HyperbolicExample {public static void main(String[] args) {double x = 1.0;// 双曲正弦(sinh(x) = (e^x - e^-x)/2)double sinh = Math.sinh(x);System.out.println("sinh(1) = " + sinh); // 输出:1.1752011936438014// 双曲余弦(cosh(x) = (e^x + e^-x)/2)double cosh = Math.cosh(x);System.out.println("cosh(1) = " + cosh); // 输出:1.5430806348152437// 双曲正切(tanh(x) = sinh(x)/cosh(x))double tanh = Math.tanh(x);System.out.println("tanh(1) = " + tanh); // 输出:0.7615941559557649}
}
4.指数与对数运算
1.指数运算:exp
、expm1
、pow
1.1. exp(double a)
:计算自然指数 e^a
System.out.println(Math.exp(1)); // 输出:2.718281828459045(e^1)
System.out.println(Math.exp(0)); // 输出:1.0(e^0)
1.2. expm1(double x)
:计算 e^x - 1
对于接近 0 的 x,该方法比 exp(x) - 1
更精确:
double x = 0.000001;
System.out.println(Math.exp(x) - 1); // 输出:1.0000005000001665e-06(精度较低)
System.out.println(Math.expm1(x)); // 输出:1.0000005000001667e-06(更精确)
1.3. pow(double a, double b)
:计算 a^b
System.out.println(Math.pow(2, 3)); // 输出:8.0(2^3)
System.out.println(Math.pow(10, -2)); // 输出:0.01(10^-2)
System.out.println(Math.pow(4, 0.5)); // 输出:2.0(4的平方根)
System.out.println(Math.pow(0, 0)); // 输出:1.0(特殊规定)
2.对数运算:log
、log10
、log1p
2.1、log(double a)
:计算自然对数 ln(a)
System.out.println(Math.log(Math.E)); // 输出:1.0(ln(e))
System.out.println(Math.log(1)); // 输出:0.0(ln(1))
2.2、log10(double a)
:计算以 10 为底的对数 log10(a)
System.out.println(Math.log10(100)); // 输出:2.0(log10(100))
System.out.println(Math.log10(0.1)); // 输出:-1.0(log10(0.1))
2.3、log1p(double x)
:计算 ln(1 + x)
对于接近 0 的 x,比 log(1 + x)
更精确:
double x = 0.000001;
System.out.println(Math.log(1 + x)); // 输出:9.999995000003333e-07(精度较低)
System.out.println(Math.log1p(x)); // 输出:9.999995000001666e-07(更精确)
三、其他实用方法
1、平方根与立方根:sqrt
、cbrt
sqrt(double a)
:返回非负 double 的正平方根(正确舍入)。cbrt(double a)
:返回 double 的立方根(可正可负)。
2、随机数生成:random
Math.random()
返回一个大于等于 0.0 且小于 1.0 的随机 double 值,每次调用返回不同结果:
// 生成 1-100 之间的随机整数
int randomInt = (int) (Math.random() * 100) + 1;
System.out.println("随机整数(1-100):" + randomInt);
注意:
Math.random()
是线程安全的,但在多线程环境下性能较差。若需高性能随机数,可考虑java.util.Random
或ThreadLocalRandom
。
3、符号与 ulp 操作
signum
:返回参数的符号(正为 1.0,负为 -1.0,零为 0.0)。ulp
:返回参数的 ulp(单位在最后一位)大小,用于衡量浮点精度。
// signum 示例
System.out.println(Math.signum(5.2)); // 输出:1.0
System.out.println(Math.signum(-3.8)); // 输出:-1.0
System.out.println(Math.signum(0.0)); // 输出:0.0// ulp 示例
System.out.println(Math.ulp(1.0)); // 输出:2.220446049250313e-16(1.0 的 ulp)
System.out.println(Math.ulp(1000000.0)); // 输出:0.0001220703125(大数的 ulp 更大,精度更低)
ulp
全称 Unit in the Last Place(最后一位的单位),是衡量浮点数精度的核心指标。
浮点数(如
float
、double
)在计算机中是离散表示的(而非连续的实数),因为它们的存储位数有限(例如 IEEE 754 单精度浮点数用 23 位表示尾数,双精度用 52 位)。这意味着:两个相邻的、可被精确表示的浮点数之间存在一个最小差值,这个差值就是该浮点数的ulp
。举个例子:
- 对于单精度浮点数(
float
):
当数值较小时(如1.0f
),相邻可表示的浮点数非常接近,ulp(1.0f)
约为1.19e-7
(即 2−23,因为单精度尾数是 23 位);
当数值很大时(如 223 附近),ulp
会变大(此时ulp(2^23)
为1.0f
),因为尾数的有限位数无法再表示更小的间隔 —— 这就是浮点数 “精度随数值增大而下降” 的原因。
ulp
是浮点数精度的 “尺子”,主要用于数值计算的误差分析和合理比较浮点数,具体场景包括:1. 衡量浮点数的 “接近程度”(替代直接相等判断)
浮点数计算中,由于舍入误差(如
0.1 + 0.2
不等于0.3
),直接用==
比较是否相等往往不准确。此时可以用ulp
判断两个数是否 “足够接近”:
如果两个数的差值小于 k 倍的ulp
(k 通常取 1 或 2),则认为它们在数值上是等效的。// 判断 a 和 b 是否接近(差值 < 2 倍 ulp) boolean isClose(double a, double b) {return Math.abs(a - b) < 2 * Math.ulp(Math.min(a, b)); }
2. 分析数值算法的精度
在科学计算(如物理模拟、工程计算)中,需要评估算法的误差是否在可接受范围内。
ulp
可以量化误差大小:
- 若计算结果与理论值的差值在 1-2 个
ulp
内,说明算法精度极高(接近浮点数的理论极限);- 若误差达到 100 个
ulp
以上,可能需要优化算法(如减少舍入累积)。3. 调试浮点计算异常
当数值计算出现 “奇怪的偏差” 时,
ulp
可以帮助定位问题:
例如,若一个本应精确的计算(如2.0 / 2.0
)结果与理论值的差值超过 1 个ulp
,可能是代码中存在类型转换错误(如float
和double
混用)或溢出。
signum
函数的作用是提取数值的 “符号信息”(正 / 负 / 零),实际用途集中在 “需要根据方向做决策” 的场景
四、资料来源
Math类
还有一些我会继续加入进来的……