目 录CONTENT

文章目录

CSharp(二十)Math 类详解

CSharp(二十)Math 类详解

一、Math 类概述

System.Math 类是 C# 中最常用的数学工具类之一。它是一个静态类,所有方法都是静态的,无需实例化即可直接调用。

核心特点

特点 说明
命名空间 System(无需额外 using,默认已包含)
类型 静态类(public static class Math
参数类型 主要接受 double 类型,部分有重载
返回值 大多返回 double,部分返回整数或布尔值
常量 提供 PIE 两个数学常量

两个静态常量

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 → 最近的整数是 23,选偶数 2
  • 3.5 → 最近的整数是 34,选偶数 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  (也可以比较字符)

有大量重载:bytesbyteshortushortintuintlongulongfloatdoubledecimal

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

最佳实践总结

  1. 三角函数参数始终使用弧度,角度需先转换
  2. 浮点数比较使用容差,不要直接用 ==
  3. NaN 判断用 double.IsNaN(),不要用 ==
  4. 需要传统四舍五入时指定 MidpointRounding.AwayFromZero
  5. 大整数取绝对值前先转为 long
  6. 整数除法前确保至少一个操作数是浮点数
  7. 使用 Math.Clamp 代替手工 if 判断范围(.NET Core 2.0+)
  8. 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 的高发区。

0
博主关闭了当前页面的评论