CSharp(二十)Math 类详解
一、Math 类概述
System.Math 类是 C# 中最常用的数学工具类之一。它是一个静态类,所有方法都是静态的,无需实例化即可直接调用。
核心特点
| 特点 | 说明 |
|---|---|
| 命名空间 | System(无需额外 using,默认已包含) |
| 类型 | 静态类(public static class Math) |
| 参数类型 | 主要接受 double 类型,部分有重载 |
| 返回值 | 大多返回 double,部分返回整数或布尔值 |
| 常量 | 提供 PI 和 E 两个数学常量 |
两个静态常量
using System;
class Program
{
static void Main()
{
// 圆周率 π
Console.WriteLine($"Math.PI = {Math.PI}");
// 输出: Math.PI = 3.141592653589793
// 自然常数 e
Console.WriteLine($"Math.E = {Math.E}");
// 输出: Math.E = 2.718281828459045
}
}
二、取整相关方法
这是日常开发中最常用的方法组,包含 6 个取整相关方法。
2.1 Math.Ceiling —— 向上取整(天花板)
返回大于或等于指定数字的最小整数。也就是往数轴的正无穷方向取整。
Console.WriteLine(Math.Ceiling(3.14)); // 输出: 4
Console.WriteLine(Math.Ceiling(3.99)); // 输出: 4
Console.WriteLine(Math.Ceiling(3.00)); // 输出: 3 (刚好是整数,不变)
Console.WriteLine(Math.Ceiling(-3.14)); // 输出: -3 (-3 > -3.14)
Console.WriteLine(Math.Ceiling(-3.99)); // 输出: -3 (-3 > -3.99)
教学要点:对于负数,向上取整的结果绝对值变小。-3.99 向上取整为 -3,因为 -3 在数轴上更大。
2.2 Math.Floor —— 向下取整(地板)
返回小于或等于指定数字的最大整数。也就是往数轴的负无穷方向取整。
Console.WriteLine(Math.Floor(3.14)); // 输出: 3
Console.WriteLine(Math.Floor(3.99)); // 输出: 3
Console.WriteLine(Math.Floor(3.00)); // 输出: 3
Console.WriteLine(Math.Floor(-3.14)); // 输出: -4 (-4 < -3.14)
Console.WriteLine(Math.Floor(-3.99)); // 输出: -4 (-4 < -3.99)
教学要点:对于负数,向下取整的结果绝对值变大。-3.14 向下取整为 -4。
2.3 Math.Round —— 四舍五入
将值舍入到最接近的整数或指定的小数位数。这是最常用的取整方法,但也是最容易踩坑的。
基本用法:
Console.WriteLine(Math.Round(3.14)); // 输出: 3
Console.WriteLine(Math.Round(3.56)); // 输出: 4
Console.WriteLine(Math.Round(3.50)); // 输出: 4 (注意!.5 向上舍入)
指定小数位数:
Console.WriteLine(Math.Round(3.14159, 2)); // 输出: 3.14
Console.WriteLine(Math.Round(3.14159, 4)); // 输出: 3.1416
Console.WriteLine(Math.Round(12345.6789, 2)); // 输出: 12345.68
银行家舍入法(MidpointRounding)—— 重要!
Math.Round 默认使用银行家舍入法(MidpointRounding.ToEven),即 .5 时舍入到最近的偶数:
Console.WriteLine(Math.Round(2.5)); // 输出: 2 ← 不是 3!
Console.WriteLine(Math.Round(3.5)); // 输出: 4 ← 不是期望的 4?等等,这就是 4
为什么? 银行家舍入规则:
2.5→ 最近的整数是2和3,选偶数23.5→ 最近的整数是3和4,选偶数4
更多银行家舍入示例:
Console.WriteLine(Math.Round(1.5)); // 输出: 2 (1 和 2 中,选偶数 2)
Console.WriteLine(Math.Round(2.5)); // 输出: 2 (2 和 3 中,选偶数 2)
Console.WriteLine(Math.Round(3.5)); // 输出: 4 (3 和 4 中,选偶数 4)
Console.WriteLine(Math.Round(4.5)); // 输出: 4 (4 和 5 中,选偶数 4)
Console.WriteLine(Math.Round(5.5)); // 输出: 6 (5 和 6 中,选偶数 6)
如果需要传统的"四舍五入":
// 使用 MidpointRounding.AwayFromZero —— 经典四舍五入
Console.WriteLine(Math.Round(2.5, MidpointRounding.AwayFromZero)); // 输出: 3
Console.WriteLine(Math.Round(3.5, MidpointRounding.AwayFromZero)); // 输出: 4
// 结合小数位数
Console.WriteLine(Math.Round(2.345, 2, MidpointRounding.AwayFromZero)); // 输出: 2.35
Console.WriteLine(Math.Round(2.345, 2, MidpointRounding.ToEven)); // 输出: 2.34
三种 MidpointRounding 模式对比:
double[] values = { 1.5, 2.5, 3.5, 4.5, 5.5 };
Console.WriteLine("=== 银行家舍入 (ToEven, 默认) ===");
foreach (var v in values)
Console.WriteLine($"{v} → {Math.Round(v, MidpointRounding.ToEven)}");
Console.WriteLine("\n=== 经典四舍五入 (AwayFromZero) ===");
foreach (var v in values)
Console.WriteLine($"{v} → {Math.Round(v, MidpointRounding.AwayFromZero)}");
// .NET Core 3.0+ 支持
Console.WriteLine("\n=== 向零舍入 (ToZero) ===");
foreach (var v in values)
Console.WriteLine($"{v} → {Math.Round(v, MidpointRounding.ToZero)}");
输出:
=== 银行家舍入 (ToEven, 默认) ===
1.5 → 2
2.5 → 2
3.5 → 4
4.5 → 4
5.5 → 6
=== 经典四舍五入 (AwayFromZero) ===
1.5 → 2
2.5 → 3
3.5 → 4
4.5 → 5
5.5 → 6
=== 向零舍入 (ToZero) ===
1.5 → 1
2.5 → 2
3.5 → 3
4.5 → 4
5.5 → 5
2.4 Math.Truncate —— 截断取整
直接丢弃小数部分,保留整数部分。相当于向零取整。
Console.WriteLine(Math.Truncate(3.14)); // 输出: 3
Console.WriteLine(Math.Truncate(3.99)); // 输出: 3
Console.WriteLine(Math.Truncate(-3.14)); // 输出: -3
Console.WriteLine(Math.Truncate(-3.99)); // 输出: -3
Truncate vs Floor 的区别(关键考点):
// 正数:Truncate 和 Floor 结果相同
Console.WriteLine(Math.Truncate(3.7)); // 3
Console.WriteLine(Math.Floor(3.7)); // 3
// 负数:Truncate 和 Floor 结果不同!
Console.WriteLine(Math.Truncate(-3.7)); // -3 (截断,丢弃小数)
Console.WriteLine(Math.Floor(-3.7)); // -4 (向下,往负无穷)
2.5 Math.Max —— 取两个值中的最大值
Console.WriteLine(Math.Max(10, 20)); // 输出: 20
Console.WriteLine(Math.Max(-5, -10)); // 输出: -5
Console.WriteLine(Math.Max(3.14, 3.15)); // 输出: 3.15
Console.WriteLine(Math.Max('A', 'B')); // 输出: B (也可以比较字符)
有大量重载:byte、sbyte、short、ushort、int、uint、long、ulong、float、double、decimal。
2.6 Math.Min —— 取两个值中的最小值
Console.WriteLine(Math.Min(10, 20)); // 输出: 10
Console.WriteLine(Math.Min(-5, -10)); // 输出: -10
Console.WriteLine(Math.Min(3.14, 3.15)); // 输出: 3.14
同样支持所有数值类型重载。
三、绝对值与符号
3.1 Math.Abs —— 取绝对值
Console.WriteLine(Math.Abs(100)); // 输出: 100
Console.WriteLine(Math.Abs(-100)); // 输出: 100
Console.WriteLine(Math.Abs(0)); // 输出: 0
Console.WriteLine(Math.Abs(-3.14)); // 输出: 3.14
Console.WriteLine(Math.Abs(-0.001)); // 输出: 0.001
注意陷阱:
// int.MinValue 的绝对值会溢出!
int minValue = int.MinValue; // -2147483648
// Math.Abs(minValue); // 会抛出 OverflowException!
// 安全做法:
Console.WriteLine(Math.Abs((long)int.MinValue)); // 先转为更大的类型
3.2 Math.Sign —— 取符号
返回一个整数表示数值的符号:
- 正数返回
1 - 零返回
0 - 负数返回
-1
Console.WriteLine(Math.Sign(100)); // 输出: 1
Console.WriteLine(Math.Sign(0)); // 输出: 0
Console.WriteLine(Math.Sign(-100)); // 输出: -1
Console.WriteLine(Math.Sign(-3.14)); // 输出: -1
// 实际应用:判断方向
double diff = b - a;
int direction = Math.Sign(diff);
// direction == 1 → b > a
// direction == 0 → b == a
// direction == -1 → b < a
四、幂运算、指数与对数
4.1 Math.Pow —— 乘方(幂运算)
返回 x 的 y 次幂:x^y
Console.WriteLine(Math.Pow(2, 3)); // 输出: 8(2³)
Console.WriteLine(Math.Pow(2, 10)); // 输出: 1024(2¹⁰)
Console.WriteLine(Math.Pow(10, 6)); // 输出: 1000000(10⁶)
// 开平方(等效)
Console.WriteLine(Math.Pow(9, 0.5)); // 输出: 3(9的平方根)
Console.WriteLine(Math.Pow(27, 1.0/3)); // 输出: 3(27的立方根)
// 分数幂
Console.WriteLine(Math.Pow(4, 0.5)); // 输出: 2(等价于 Sqrt(4))
Console.WriteLine(Math.Pow(4, -1)); // 输出: 0.25(4的倒数)
// 特殊情况
Console.WriteLine(Math.Pow(0, 0)); // 输出: 1(数学惯例)
Console.WriteLine(Math.Pow(0, 5)); // 输出: 0
Console.WriteLine(Math.Pow(5, 0)); // 输出: 1(任何数的0次幂为1)
Console.WriteLine(Math.Pow(-2, 3)); // 输出: -8(负数的奇数次幂为负)
Console.WriteLine(Math.Pow(-2, 2)); // 输出: 4(负数的偶数次幂为正)
4.2 Math.Sqrt —— 平方根
Console.WriteLine(Math.Sqrt(4)); // 输出: 2
Console.WriteLine(Math.Sqrt(9)); // 输出: 3
Console.WriteLine(Math.Sqrt(2)); // 输出: 1.4142135623730951
Console.WriteLine(Math.Sqrt(0)); // 输出: 0
Console.WriteLine(Math.Sqrt(100)); // 输出: 10
// 负数会返回 NaN(不是数字)
Console.WriteLine(Math.Sqrt(-1)); // 输出: NaN
4.3 Math.Cbrt —— 立方根(.NET Core 2.1+)
Console.WriteLine(Math.Cbrt(27)); // 输出: 3
Console.WriteLine(Math.Cbrt(8)); // 输出: 2
Console.WriteLine(Math.Cbrt(-8)); // 输出: -2
Console.WriteLine(Math.Cbrt(125)); // 输出: 5
4.4 Math.Exp —— e 的幂
返回 e 的 x 次幂(e^x)。
Console.WriteLine(Math.Exp(1)); // 输出: 2.718281828459045(即 e¹)
Console.WriteLine(Math.Exp(2)); // 输出: 7.38905609893065(即 e²)
Console.WriteLine(Math.Exp(0)); // 输出: 1(任何数的0次幂)
Console.WriteLine(Math.Exp(-1)); // 输出: 0.36787944117144233
4.5 Math.Log —— 自然对数(以 e 为底)
Console.WriteLine(Math.Log(Math.E)); // 输出: 1(logₑe = 1)
Console.WriteLine(Math.Log(1)); // 输出: 0(logₑ1 = 0)
Console.WriteLine(Math.Log(10)); // 输出: 2.302585092994046
// 负数返回 NaN
Console.WriteLine(Math.Log(-1)); // 输出: NaN
4.6 Math.Log10 —— 常用对数(以 10 为底)
Console.WriteLine(Math.Log10(10)); // 输出: 1(log₁₀10 = 1)
Console.WriteLine(Math.Log10(100)); // 输出: 2(log₁₀100 = 2)
Console.WriteLine(Math.Log10(1000)); // 输出: 3(log₁₀1000 = 3)
Console.WriteLine(Math.Log10(1)); // 输出: 0
4.7 Math.Log2 —— 以 2 为底的对数(.NET Core 2.1+)
Console.WriteLine(Math.Log2(2)); // 输出: 1
Console.WriteLine(Math.Log2(8)); // 输出: 3(因为 2³ = 8)
Console.WriteLine(Math.Log2(1024)); // 输出: 10(因为 2¹⁰ = 1024)
Console.WriteLine(Math.Log2(1)); // 输出: 0
4.8 Math.Log(x, y) —— 以任意底数的对数
Console.WriteLine(Math.Log(8, 2)); // 输出: 3(log₂8 = 3)
Console.WriteLine(Math.Log(100, 10)); // 输出: 2(log₁₀100 = 2)
Console.WriteLine(Math.Log(27, 3)); // 输出: 3(log₃27 = 3)
五、三角函数
所有三角函数的参数都是弧度制(radian),不是角度。换算公式:
弧度 = 角度 × (π / 180)
角度 = 弧度 × (180 / π)
5.1 基本三角函数
// Sin —— 正弦
Console.WriteLine(Math.Sin(0)); // 输出: 0
Console.WriteLine(Math.Sin(Math.PI / 2)); // 输出: 1(sin 90°)
Console.WriteLine(Math.Sin(Math.PI / 6)); // 输出: 0.5(sin 30°)
Console.WriteLine(Math.Sin(Math.PI / 4)); // 输出: 0.7071...(sin 45°)
// Cos —— 余弦
Console.WriteLine(Math.Cos(0)); // 输出: 1
Console.WriteLine(Math.Cos(Math.PI)); // 输出: -1(cos 180°)
Console.WriteLine(Math.Cos(Math.PI / 3)); // 输出: 0.5(cos 60°)
// Tan —— 正切
Console.WriteLine(Math.Tan(0)); // 输出: 0
Console.WriteLine(Math.Tan(Math.PI / 4)); // 输出: 1(tan 45° ≈ 1)
5.2 反三角函数
// Asin —— 反正弦,返回值范围 [-π/2, π/2]
Console.WriteLine(Math.Asin(1)); // 输出: 1.5707963267948966(即 π/2)
Console.WriteLine(Math.Asin(0.5)); // 输出: 0.5235987755982989(即 π/6,30°)
// Acos —— 反余弦,返回值范围 [0, π]
Console.WriteLine(Math.Acos(1)); // 输出: 0
Console.WriteLine(Math.Acos(0.5)); // 输出: 1.0471975511965979(即 π/3,60°)
// Atan —— 反正切,返回值范围 [-π/2, π/2]
Console.WriteLine(Math.Atan(1)); // 输出: 0.7853981633974483(即 π/4,45°)
Console.WriteLine(Math.Atan(0)); // 输出: 0
5.3 Math.Atan2 —— 双参数反正切(重要!)
Math.Atan2(y, x) 返回从原点 (0,0) 到点 (x,y) 的向量与 x 轴正方向的夹角,返回值范围是 [-π, π],能区分象限。
// 第一象限
Console.WriteLine(Math.Atan2(1, 1)); // 输出: 0.785398...(π/4,45°)
// 第二象限
Console.WriteLine(Math.Atan2(1, -1)); // 输出: 2.35619...(3π/4,135°)
// 第三象限
Console.WriteLine(Math.Atan2(-1, -1)); // 输出: -2.35619...(-3π/4,-135°)
// 第四象限
Console.WriteLine(Math.Atan2(-1, 1)); // 输出: -0.78539...(-π/4,-45°)
// 坐标轴上的特殊情况
Console.WriteLine(Math.Atan2(0, 1)); // 输出: 0
Console.WriteLine(Math.Atan2(1, 0)); // 输出: 1.570796...(π/2,90°)
Console.WriteLine(Math.Atan2(0, -1)); // 输出: 3.14159...(π,180°)
Console.WriteLine(Math.Atan2(-1, 0)); // 输出: -1.570796...(-π/2,-90°)
实用场景:计算两点之间的角度
// 计算从点 A 到点 B 的方位角
double Ax = 0, Ay = 0;
double Bx = 10, By = 10;
double angleRad = Math.Atan2(By - Ay, Bx - Ax);
double angleDeg = angleRad * 180 / Math.PI;
Console.WriteLine($"方位角: {angleDeg}°"); // 输出: 45°
5.4 弧度与角度的实用转换工具
/// <summary>
/// 角度转弧度
/// </summary>
public static double DegreesToRadians(double degrees)
{
return degrees * Math.PI / 180.0;
}
/// <summary>
/// 弧度转角度
/// </summary>
public static double RadiansToDegrees(double radians)
{
return radians * 180.0 / Math.PI;
}
// 使用示例
Console.WriteLine(Math.Sin(DegreesToRadians(90))); // 输出: 1
Console.WriteLine(Math.Cos(DegreesToRadians(60))); // 输出: 0.5
六、双曲函数
双曲函数在工程、物理和某些算法中有重要应用。
// Sinh —— 双曲正弦
Console.WriteLine(Math.Sinh(0)); // 输出: 0
Console.WriteLine(Math.Sinh(1)); // 输出: 1.1752011936438014
// Cosh —— 双曲余弦
Console.WriteLine(Math.Cosh(0)); // 输出: 1
Console.WriteLine(Math.Cosh(1)); // 输出: 1.5430806348152437
// Tanh —— 双曲正切
Console.WriteLine(Math.Tanh(0)); // 输出: 0
Console.WriteLine(Math.Tanh(1)); // 输出: 0.7615941559557649
Console.WriteLine(Math.Tanh(100)); // 输出: 1(趋近于 1)
七、夹值与比较
7.1 Math.Clamp —— 将值限制在指定范围内(.NET Core 2.0+)
// 如果值小于最小值,返回最小值
Console.WriteLine(Math.Clamp(5, 10, 20)); // 输出: 10
// 如果值大于最大值,返回最大值
Console.WriteLine(Math.Clamp(30, 10, 20)); // 输出: 20
// 如果值在范围内,返回原值
Console.WriteLine(Math.Clamp(15, 10, 20)); // 输出: 15
// 实用场景:限制玩家血量
int hp = 120;
hp = Math.Clamp(hp, 0, 100); // hp 现在是 100
// 实用场景:安全数组索引
int index = Math.Clamp(userInput, 0, arrayLength - 1);
7.2 无 Clamp 时的替代实现(.NET Framework 旧版本)
// 手工实现 Clamp
public static double Clamp(double value, double min, double max)
{
if (value < min) return min;
if (value > max) return max;
return value;
}
// 或者用 Min/Max 组合实现
public static double Clamp(double value, double min, double max)
{
return Math.Max(min, Math.Min(value, max));
}
八、特殊数学值判断
8.1 三个"非数字"标志值
// Double.NaN —— 非数字
double nan = Double.NaN;
// Double.PositiveInfinity —— 正无穷大
double posInf = Double.PositiveInfinity;
// Double.NegativeInfinity —— 负无穷大
double negInf = Double.NegativeInfinity;
8.2 判断这三个特殊值
double a = double.NaN;
double b = double.PositiveInfinity;
double c = double.NegativeInfinity;
double d = Math.Sqrt(-1); // 也是 NaN
double e = 1.0 / 0.0; // 正无穷
// 判断 NaN —— 注意:不能用 == 比较!
Console.WriteLine(a == double.NaN); // 输出: False!(永远 False)
Console.WriteLine(double.IsNaN(a)); // 输出: True(正确做法)
Console.WriteLine(double.IsNaN(d)); // 输出: True
// 判断正无穷
Console.WriteLine(double.IsPositiveInfinity(b)); // 输出: True
Console.WriteLine(double.IsPositiveInfinity(e)); // 输出: True
// 判断负无穷
Console.WriteLine(double.IsNegativeInfinity(c)); // 输出: True
// 判断任意无穷
Console.WriteLine(double.IsInfinity(b)); // 输出: True
Console.WriteLine(double.IsInfinity(c)); // 输出: True
Console.WriteLine(double.IsInfinity(a)); // 输出: False
// 判断普通有限数
Console.WriteLine(double.IsFinite(3.14)); // 输出: True (.NET Core 7+)
Console.WriteLine(double.IsFinite(a)); // 输出: False
重点:NaN 的比较陷阱
double nan = double.NaN;
// 所有与 NaN 的比较都返回 false,包括与自身比较!
Console.WriteLine(nan == double.NaN); // False
Console.WriteLine(nan != double.NaN); // True
Console.WriteLine(nan > 0); // False
Console.WriteLine(nan < 0); // False
Console.WriteLine(nan == nan); // False ← 自己不等于自己!
// NaN 也不是任何数字
Console.WriteLine(double.IsNaN(nan)); // True ← 只能用这个判断
九、位运算与进制相关
9.1 Math.BigMul —— 64 位乘法(防溢出)
两个 int(32位)相乘可能溢出,Math.BigMul 返回 long(64位)保证不溢出:
int a = int.MaxValue;
int b = 2;
// 直接用 * 会溢出(在 unchecked 上下文中)
// long unsafeResult = a * b; // 结果错误!
// 安全做法:
long safeResult = Math.BigMul(a, b);
Console.WriteLine($"安全结果: {safeResult}");
// 输出: 安全结果: 4294967294
9.2 Math.DivRem —— 同时获取商和余数
int quotient;
int remainder = Math.DivRem(17, 5, out quotient);
Console.WriteLine($"17 ÷ 5 = {quotient} 余 {remainder}");
// 输出: 17 ÷ 5 = 3 余 2
// 也可以只取余数(忽略商):
int rem = Math.DivRem(17, 5, out _);
Console.WriteLine($"余数: {rem}"); // 输出: 余数: 2
9.3 Math.ILogB —— 以 2 为底的整数对数
Console.WriteLine(Math.ILogB(8)); // 输出: 3(因为 2³ = 8)
Console.WriteLine(Math.ILogB(16)); // 输出: 4
Console.WriteLine(Math.ILogB(1024)); // 输出: 10
Console.WriteLine(Math.ILogB(1)); // 输出: 0
9.4 Math.ScaleB —— 按 2 的幂缩放(.NET Core 3.0+)
Math.ScaleB(x, n) = x × 2ⁿ
Console.WriteLine(Math.ScaleB(3, 2)); // 输出: 12(3 × 2² = 3 × 4)
Console.WriteLine(Math.ScaleB(5, 3)); // 输出: 40(5 × 2³ = 5 × 8)
Console.WriteLine(Math.ScaleB(1, 10)); // 输出: 1024(1 × 2¹⁰)
Console.WriteLine(Math.ScaleB(10, -1)); // 输出: 5(10 × 2⁻¹ = 10 ÷ 2)
十、位复制与浮点数底层操作
10.1 Math.BitIncrement / Math.BitDecrement —— 调整浮点数的最低位
// BitIncrement: 返回比给定值大 1 个 ULP(最小精度单位)的值
Console.WriteLine(Math.BitIncrement(1.0));
// 输出: 1.0000000000000002
Console.WriteLine(Math.BitDecrement(1.0));
// 输出: 0.9999999999999999
10.2 Math.CopySign —— 复制符号
将一个数的符号复制到另一个数的绝对值上:
Console.WriteLine(Math.CopySign(3.14, -100.0)); // 输出: -3.14
Console.WriteLine(Math.CopySign(-5.0, 10.0)); // 输出: 5.0
Console.WriteLine(Math.CopySign(0.0, -1.0)); // 输出: -0.0
10.3 Math.FusedMultiplyAdd —— 融合乘加(.NET Core 3.0+)
高效执行 (x * y) + z,只舍入一次,比分开计算更精确:
// 标准做法(两次舍入,可能有误差)
double result1 = (1.5 * 2.0) + 0.5; // 输出: 3.5
// FMA 做法(一次舍入,更精确)
double result2 = Math.FusedMultiplyAdd(1.5, 2.0, 0.5); // 输出: 3.5
10.4 Math.IEEERemainder —— IEEE 754 标准取余
与 % 运算符不同,IEEERemainder 遵循 IEEE 754 浮点标准:
Console.WriteLine(Math.IEEERemainder(10, 3)); // 输出: 1
Console.WriteLine(Math.IEEERemainder(10, 4)); // 输出: 2
Console.WriteLine(Math.IEEERemainder(10.5, 4)); // 输出: 2.5
// 与 % 的区别(对负数的处理不同)
Console.WriteLine(Math.IEEERemainder(-7, 3)); // 输出: -1
Console.WriteLine(-7 % 3); // 输出: -1(这里恰好相同)
Console.WriteLine(Math.IEEERemainder(7, -3)); // 输出: 1
Console.WriteLine(7 % -3); // 输出: 1
十一、常用数学公式实现示例
计算直角三角形的斜边
/// <summary>
/// 勾股定理:c² = a² + b²
/// </summary>
public static double Hypotenuse(double a, double b)
{
return Math.Sqrt(a * a + b * b);
}
// 等效于 .NET Core 2.1+ 的内置方法
Console.WriteLine(Math.Hypot(3, 4)); // 输出: 5
计算两点之间的距离
/// <summary>
/// 欧几里得距离:sqrt((x2-x1)² + (y2-y1)²)
/// </summary>
public static double Distance(double x1, double y1, double x2, double y2)
{
double dx = x2 - x1;
double dy = y2 - y1;
return Math.Sqrt(dx * dx + dy * dy);
}
// 使用
double dist = Distance(0, 0, 3, 4);
Console.WriteLine($"距离: {dist}"); // 输出: 5
线性插值(Lerp)
/// <summary>
/// 线性插值:value = a + (b - a) * t
/// </summary>
public static double Lerp(double a, double b, double t)
{
t = Math.Clamp(t, 0, 1); // 限制 t 在 [0, 1] 范围内
return a + (b - a) * t;
}
// 使用示例
Console.WriteLine(Lerp(0, 100, 0.5)); // 输出: 50(中点)
Console.WriteLine(Lerp(0, 100, 0.25)); // 输出: 25
Console.WriteLine(Lerp(0, 100, 0)); // 输出: 0
Console.WriteLine(Lerp(0, 100, 1)); // 输出: 100
// 应用:平滑颜色过渡
double red = Lerp(255, 0, 0.5); // 从红色过渡到黑色的一半
Console.WriteLine($"红色分量: {red}"); // 输出: 127.5
判断一个数是否为质数
public static bool IsPrime(int n)
{
if (n <= 1) return false;
if (n == 2) return true;
if (n % 2 == 0) return false;
int limit = (int)Math.Floor(Math.Sqrt(n));
for (int i = 3; i <= limit; i += 2)
{
if (n % i == 0) return false;
}
return true;
}
计算复利
/// <summary>
/// 计算复利终值
/// </summary>
/// <param name="principal">本金</param>
/// <param name="rate">年利率(如 0.05 表示 5%)</param>
/// <param name="years">年数</param>
/// <param name="timesPerYear">每年计息次数</param>
public static double CompoundInterest(double principal, double rate, double years, int timesPerYear = 1)
{
return principal * Math.Pow(1 + rate / timesPerYear, timesPerYear * years);
}
// 示例:1万元,年利率5%,存10年
double result = CompoundInterest(10000, 0.05, 10);
Console.WriteLine($"复利后金额: {result:F2}");
// 输出: 复利后金额: 16288.95
归一化向量
public static (double x, double y) Normalize(double x, double y)
{
double length = Math.Sqrt(x * x + y * y);
if (length == 0) return (0, 0); // 避免除以零
return (x / length, y / length);
}
var (nx, ny) = Normalize(3, 4);
Console.WriteLine($"归一化: ({nx:F4}, {ny:F4})");
// 输出: 归一化: (0.6000, 0.8000)
Console.WriteLine($"验证长度: {Math.Sqrt(nx * nx + ny * ny)}"); // 输出: 1
十二、完整方法速查表
| 分类 | 方法 | 说明 | 示例 |
|---|---|---|---|
| 常量 | PI |
圆周率 π | Math.PI → 3.1415926... |
E |
自然常数 e | Math.E → 2.7182818... |
|
| 取整 | Ceiling(x) |
向上取整 | Ceiling(3.1) → 4 |
Floor(x) |
向下取整 | Floor(3.9) → 3 |
|
Round(x) |
四舍五入 | Round(3.5) → 4 |
|
Round(x, n) |
保留 n 位小数 | Round(3.1415, 2) → 3.14 |
|
Truncate(x) |
截断小数 | Truncate(3.9) → 3 |
|
Max(a,b) |
取较大值 | Max(3, 5) → 5 |
|
Min(a,b) |
取较小值 | Min(3, 5) → 3 |
|
Clamp(x,min,max) |
限制在范围内 | Clamp(120,0,100) → 100 |
|
| 符号/绝对值 | Abs(x) |
绝对值 | Abs(-5) → 5 |
Sign(x) |
符号(-1/0/1) | Sign(-8) → -1 |
|
CopySign(x,y) |
复制 y 的符号给 x | CopySign(5,-1) → -5 |
|
| 幂/指数/对数 | Pow(x,y) |
x 的 y 次幂 | Pow(2, 10) → 1024 |
Sqrt(x) |
平方根 | Sqrt(9) → 3 |
|
Cbrt(x) |
立方根 | Cbrt(27) → 3 |
|
Exp(x) |
e 的 x 次幂 | Exp(1) → e |
|
Log(x) |
自然对数(ln) | Log(e) → 1 |
|
Log10(x) |
常用对数(lg) | Log10(100) → 2 |
|
Log2(x) |
以 2 为底的对数 | Log2(8) → 3 |
|
Log(x, y) |
以 y 为底的对数 | Log(8, 2) → 3 |
|
ILogB(x) |
底数为 2 的整数对数 | ILogB(8) → 3 |
|
ScaleB(x, n) |
x × 2ⁿ | ScaleB(3, 2) → 12 |
|
| 三角 | Sin(x) |
正弦(弧度) | Sin(π/2) → 1 |
Cos(x) |
余弦(弧度) | Cos(0) → 1 |
|
Tan(x) |
正切(弧度) | Tan(π/4) → 1 |
|
Asin(x) |
反正弦 | Asin(1) → π/2 |
|
Acos(x) |
反余弦 | Acos(1) → 0 |
|
Atan(x) |
反正切 | Atan(1) → π/4 |
|
Atan2(y,x) |
双参数反正切 | Atan2(1,1) → π/4 |
|
| 双曲 | Sinh(x) |
双曲正弦 | |
Cosh(x) |
双曲余弦 | ||
Tanh(x) |
双曲正切 | ||
| 运算 | BigMul(a,b) |
64 位乘法 | BigMul(Max,2) |
DivRem(a,b,out) |
除法取余 | DivRem(17,5) → 3余2 |
|
FusedMultiplyAdd |
x*y+z 精确计算 |
||
IEEERemainder |
IEEE 标准取余 | ||
| 特殊值 | BitIncrement |
加 1 个最小精度 | |
BitDecrement |
减 1 个最小精度 | ||
Hypot(a,b) |
勾股定理 | Hypot(3,4) → 5 |
十三、常见陷阱与最佳实践
陷阱一:整数除法
// 错误:整数除法会截断
double avg = 5 / 2; // 结果是 2,不是 2.5!
Console.WriteLine(avg); // 输出: 2
// 正确做法:至少有一个操作数是浮点
double avg1 = 5.0 / 2; // 2.5
double avg2 = (double)5 / 2; // 2.5
double avg3 = 5 / 2.0; // 2.5
陷阱二:浮点数比较
double a = 0.1 + 0.2;
double b = 0.3;
// 不要这样比较浮点数!
Console.WriteLine(a == b); // 输出: False!(浮点精度问题)
// 正确做法:使用容差比较
const double Epsilon = 1e-10; // 一个很小的数
bool areEqual = Math.Abs(a - b) < Epsilon;
Console.WriteLine(areEqual); // 输出: True
陷阱三:NaN 比较
double nan = double.NaN;
// 错误:永远为 false
if (nan == double.NaN) { /* 永远不会执行 */ }
// 正确:使用专用方法
if (double.IsNaN(nan)) { /* 正确判断 */ }
陷阱四:取绝对值溢出
// 错误:可能溢出
// Math.Abs(int.MinValue); // 抛出 OverflowException!
// 正确:转为更大类型
long safeAbs = Math.Abs((long)int.MinValue);
陷阱五:Round 的银行家舍入
// 默认是银行家舍入
Console.WriteLine(Math.Round(2.5)); // 输出: 2(不是 3!)
// 如果需要传统四舍五入
Console.WriteLine(Math.Round(2.5, MidpointRounding.AwayFromZero)); // 输出: 3
最佳实践总结
- 三角函数参数始终使用弧度,角度需先转换
- 浮点数比较使用容差,不要直接用
== - NaN 判断用
double.IsNaN(),不要用== - 需要传统四舍五入时指定
MidpointRounding.AwayFromZero - 大整数取绝对值前先转为
long - 整数除法前确保至少一个操作数是浮点数
- 使用
Math.Clamp代替手工if判断范围(.NET Core 2.0+) - 用
Math.Atan2(y, x)而非Math.Atan(y/x)来计算角度,前者能正确处理除零和象限问题
十四、综合练习题
练习题 1:简单计算器
编写一个程序,给定圆的半径,计算其周长、面积和体积(球体)。
double radius = 5.0;
double circumference = 2 * Math.PI * radius;
double area = Math.PI * Math.Pow(radius, 2);
double volume = (4.0 / 3.0) * Math.PI * Math.Pow(radius, 3);
Console.WriteLine($"半径: {radius}");
Console.WriteLine($"周长: {circumference:F2}");
Console.WriteLine($"圆面积: {area:F2}");
Console.WriteLine($"球体积: {volume:F2}");
练习题 2:生成指定范围内的随机整数
Random random = new Random();
// 生成 [min, max) 的整数(不含 max)
int RandomRange(int min, int max)
{
return random.Next(min, max);
}
// 生成 [min, max] 的整数(含 max)
int RandomRangeInclusive(int min, int max)
{
return random.Next(min, max + 1);
}
// 用 Math 验证结果在范围内
int result = RandomRangeInclusive(1, 6); // 模拟骰子
Console.WriteLine($"骰子点数: {result}");
Console.WriteLine($"在 1-6 范围内: {result >= 1 && result <= 6}");
练习题 3:求三角形面积(海伦公式)
/// <summary>
/// 海伦公式:S = √[p(p-a)(p-b)(p-c)],其中 p = (a+b+c)/2
/// </summary>
double TriangleArea(double a, double b, double c)
{
double p = (a + b + c) / 2;
return Math.Sqrt(p * (p - a) * (p - b) * (p - c));
}
Console.WriteLine(TriangleArea(3, 4, 5)); // 输出: 6(直角三角形)
练习题 4:计算角度之间的最小差
/// <summary>
/// 返回两个角度之间的最小差值(0-180度)
/// </summary>
double AngleDifference(double angle1, double angle2)
{
double diff = Math.Abs(angle1 - angle2) % 360;
return diff > 180 ? 360 - diff : diff;
}
Console.WriteLine(AngleDifference(10, 350)); // 输出: 20
Console.WriteLine(AngleDifference(90, 270)); // 输出: 180
Console.WriteLine(AngleDifference(45, 135)); // 输出: 90
十五、.NET 版本差异说明
| 方法 | 最低版本 | 说明 |
|---|---|---|
Math.Pow, Math.Sqrt 等核心方法 |
.NET Framework 1.0 | 所有版本都支持 |
Math.Clamp |
.NET Core 2.0 | Framework 4.x 不可用 |
Math.Cbrt |
.NET Core 2.1 | 立方根 |
Math.Hypot |
.NET Core 2.1 | 勾股定理 |
Math.Log2 |
.NET Core 2.1 | 以 2 为底的对数 |
Math.BitIncrement / BitDecrement |
.NET Core 3.0 | 浮点最低位调整 |
Math.FusedMultiplyAdd |
.NET Core 3.0 | 融合乘加 |
Math.ScaleB |
.NET Core 3.0 | 按 2 的幂缩放 |
Math.CopySign |
.NET Core 3.0 | 复制符号 |
Math.ILogB |
.NET Core 3.0 | 整数对数 |
double.IsFinite |
.NET Core 7 | 判断有限数 |
MidpointRounding.ToZero |
.NET Core 3.0 | 第三个舍入模式 |
总结:
System.Math类提供了从基础四则运算到高级数学函数的一整套工具。掌握它的使用,可以优雅地解决日常开发中绝大多数数学计算需求。特别要注意取整方法的差异、浮点数比较陷阱、以及银行家舍入这些细节——它们往往是面试和 Bug 的高发区。