Go基础:常用数学函数处理(主要是math包rand包的处理)
文章目录
- 一、`math` 包概览
- 1.1 重要数学常量
- 1.2 基本四则运算与取整
- 1.3 指数与对数函数
- 1.4 三角函数
- 1.5 其他实用函数
- 1.6 浮点数处理的特殊函数
- 二、随机数
- 2.1 math/rand 包详解
- 2.2 crypto/rand 包详解
一、math
包概览
Go 语言通过内置的 math
包提供了丰富的数学运算功能,它提供了一套完整、高效且标准的数学工具。这些函数都经过高度优化,性能卓越,适用于科学计算、图形学、游戏开发等众多领域。
1.1 重要数学常量
math
包中定义了许多数学常数,这些常量在科学和工程计算中非常有用。
常量 | 描述 | 近似值 |
---|---|---|
math.Pi | 圆周率 π | 3.141592653589793 |
math.E | 自然对数的底数 e | 2.718281828459045 |
math.Phi | 黄金比例 φ | 1.618033988749895 |
math.Sqrt2 | 2 的平方根 | 1.4142135623730951 |
math.SqrtE | e 的平方根 | 1.6487212707001282 |
math.SqrtPi | π 的平方根 | 1.772453850905516 |
math.Ln2 | 2 的自然对数 | 0.6931471805599453 |
math.Ln10 | 10 的自然对数 | 2.302585092994046 |
示例:使用数学常量
package main
import ("fmt""math"
)
func main() {// 计算一个半径为 5 的圆的周长和面积radius := 5.0circumference := 2 * math.Pi * radiusarea := math.Pi * radius * radiusfmt.Printf("半径为 %.2f 的圆:\n", radius)fmt.Printf("周长: %.4f\n", circumference) // 输出: 周长: 31.4159fmt.Printf("面积: %.4f\n", area) // 输出: 面积: 78.5398// 使用自然对数底 efmt.Printf("自然对数的底数 e: %.10f\n", math.E) // 输出: 自然对数的底数 e: 2.7182818285
}
1.2 基本四则运算与取整
虽然 Go 的基本运算符 +
, -
, *
, /
可以完成四则运算,但 math
包提供了更复杂的取整功能。
函数 | 描述 | 示例 |
---|---|---|
math.Ceil(x) | 向上取整,返回不小于 x 的最小整数。 | math.Ceil(1.2) -> 2.0 |
math.Floor(x) | 向下取整,返回不大于 x 的最大整数。 | math.Floor(1.8) -> 1.0 |
math.Round(x) | 四舍五入到最近的整数。如果小数部分正好是 0.5,则向偶数方向舍入。 | math.Round(1.5) -> 2.0 , math.Round(2.5) -> 2.0 |
math.Trunc(x) | 截断小数部分,返回整数部分。 | math.Trunc(1.8) -> 1.0 |
案例代码:取整函数对比
package main
import ("fmt""math"
)
func main() {x := 3.14159y := -2.718z := 2.5 // 用于测试 Round 的特殊情况fmt.Printf("原始值: x = %.5f, y = %.5f, z = %.1f\n", x, y, z)fmt.Println("--- 向上取整 ---")fmt.Printf("math.Ceil(%.5f) = %.1f\n", x, math.Ceil(x)) // 4.0fmt.Printf("math.Ceil(%.5f) = %.1f\n", y, math.Ceil(y)) // -2.0fmt.Println("--- 向下取整 ---")fmt.Printf("math.Floor(%.5f) = %.1f\n", x, math.Floor(x)) // 3.0fmt.Printf("math.Floor(%.5f) = %.1f\n", y, math.Floor(y)) // -3.0fmt.Println("--- 四舍五入 ---")fmt.Printf("math.Round(%.5f) = %.1f\n", x, math.Round(x)) // 3.0fmt.Printf("math.Round(%.5f) = %.1f\n", y, math.Round(y)) // -3.0fmt.Printf("math.Round(%.1f) = %.1f\n", z, math.Round(z)) // 2.0 (向偶数舍入)fmt.Println("--- 截断 ---")fmt.Printf("math.Trunc(%.5f) = %.1f\n", x, math.Trunc(x)) // 3.0fmt.Printf("math.Trunc(%.5f) = %.1f\n", y, math.Trunc(y)) // -2.0
}
1.3 指数与对数函数
函数 | 描述 | 示例 |
---|---|---|
math.Pow(x, y) | 返回 x 的 y 次幂。 | math.Pow(2, 3) -> 8.0 |
math.Pow10(n) | 返回 10 的 n 次幂。 | math.Pow10(2) -> 100.0 |
math.Sqrt(x) | 返回 x 的平方根。 | math.Sqrt(16) -> 4.0 |
math.Cbrt(x) | 返回 x 的立方根。 | math.Cbrt(27) -> 3.0 |
math.Log(x) | 返回 x 的自然对数(底为 e)。 | math.Log(math.E) -> 1.0 |
math.Log10(x) | 返回 x 的常用对数(底为 10)。 | math.Log10(100) -> 2.0 |
math.Log2(x) | 返回 x 的二进制对数(底为 2)。 | math.Log2(8) -> 3.0 |
math.Exp(x) | 返回 e 的 x 次幂。 | math.Exp(1) -> 2.718... |
案例代码:计算勾股定理和连续复利
package main
import ("fmt""math"
)
func main() {// 1. 计算直角三角形的斜边 (勾股定理: a² + b² = c²)a, b := 3.0, 4.0c_squared := math.Pow(a, 2) + math.Pow(b, 2)c := math.Sqrt(c_squared)fmt.Printf("直角边为 %.1f 和 %.1f 的斜边长度为: %.1f\n", a, b, c) // 输出: 5.0// 2. 计算连续复利 (公式: P * e^(r*t))principal := 1000.0 // 本金rate := 0.05 // 年利率time := 10.0 // 年数amount := principal * math.Exp(rate*time)fmt.Printf("本金 %.2f, 年利率 %.2f, %d 年后的连续复利本息和为: %.2f\n", principal, rate, int(time), amount) // 输出: 1648.72
}
1.4 三角函数
所有三角函数的参数单位都是弧度,而不是角度。你可以使用 math.Pi
进行角度和弧度的转换。
- 角度转弧度:
radians = degrees * math.Pi / 180
- 弧度转角度:
degrees = radians * 180 / math.Pi
函数 | 描述 |
---|---|
math.Sin(x) | 正弦函数 |
math.Cos(x) | 余弦函数 |
math.Tan(x) | 正切函数 |
math.Asin(x) | 反正弦函数 |
math.Acos(x) | 反余弦函数 |
math.Atan(x) | 反正切函数 |
math.Atan2(y, x) | 返回 y/x 的反正切值,但它会利用两个参数的符号来确定结果的象限,比 Atan 更准确。 |
案例代码:计算一个 30 度角的三角函数值
package main
import ("fmt""math"
)
func main() {angleDegrees := 30.0// 将角度转换为弧度angleRadians := angleDegrees * math.Pi / 180sinVal := math.Sin(angleRadians)cosVal := math.Cos(angleRadians)tanVal := math.Tan(angleRadians)fmt.Printf("角度 %.2f 度的三角函数值:\n", angleDegrees)fmt.Printf("Sin(%.2f°) = %.4f\n", angleDegrees, sinVal) // 0.5000fmt.Printf("Cos(%.2f°) = %.4f\n", angleDegrees, cosVal) // 0.8660fmt.Printf("Tan(%.2f°) = %.4f\n", angleDegrees, tanVal) // 0.5774
}
1.5 其他实用函数
函数 | 描述 | 示例 |
---|---|---|
math.Abs(x) | 返回 x 的绝对值。 | math.Abs(-10) -> 10.0 |
math.Max(x, y) | 返回 x 和 y 中的较大值。 | math.Max(1, 2) -> 2.0 |
math.Min(x, y) | 返回 x 和 y 中的较小值。 | math.Min(1, 2) -> 1.0 |
math.Mod(x, y) | 取余运算,返回 x/y 的浮点余数。 | math.Mod(10, 3) -> 1.0 |
math.Dim(x, y) | 返回 x - y 的最大值与 0 之间的数,即 max(x-y, 0) 。 | math.Dim(5, 10) -> 0.0 |
案例代码:在一组数字中找到最大值、最小值和绝对值
package main
import ("fmt""math"
)
func main() {numbers := []float64{-42, 3.14, 0, 98.6, -17.5}if len(numbers) == 0 {fmt.Println("切片为空")return}// 初始化最大值和最小值为切片的第一个元素maxVal := numbers[0]minVal := numbers[0]// 遍历切片,使用 math.Max 和 math.Minfor _, num := range numbers[1:] {maxVal = math.Max(maxVal, num)minVal = math.Min(minVal, num)}fmt.Printf("数字切片: %v\n", numbers)fmt.Printf("最大值: %.2f\n", maxVal) // 98.60fmt.Printf("最小值: %.2f\n", minVal) // -42.00// 计算 -42 的绝对值absVal := math.Abs(numbers[0])fmt.Printf("第一个元素 %.2f 的绝对值是: %.2f\n", numbers[0], absVal) // 42.00
}
1.6 浮点数处理的特殊函数
在浮点数运算中,可能会遇到一些特殊值,如“非数字”(Not a Number, NaN)和无穷大(Infinity)。math
包提供了处理这些情况的函数。
函数/常量 | 描述 |
---|---|
math.NaN() | 返回一个“非数字”值。 |
math.Inf(sign) | 如果 sign >= 0 ,返回正无穷大;否则返回负无穷大。 |
math.IsNaN(x) | 判断 x 是否为 NaN。 |
math.IsInf(x, sign) | 判断 x 是否为无穷大。sign 的含义同 math.Inf 。 |
案例代码:处理特殊浮点值
package main
import ("fmt""math"
)
func main() {// 生成特殊值nan := math.NaN()posInf := math.Inf(1)negInf := math.Inf(-1)fmt.Printf("NaN: %v, 正无穷: %v, 负无穷: %v\n", nan, posInf, negInf)// 检查特殊值fmt.Printf("nan 是否为 NaN? %t\n", math.IsNaN(nan)) // truefmt.Printf("posInf 是否为无穷大? %t\n", math.IsInf(posInf, 1)) // true// 包含 NaN 的运算result := posInf + negInffmt.Printf("正无穷 + 负无穷 = %v\n", result) // NaNfmt.Printf("result 是否为 NaN? %t\n", math.IsNaN(result)) // true// 重要:NaN 与任何值(包括它自己)比较都返回 falsefmt.Printf("nan == nan? %t\n", nan == nan) // false// 正确的判断方式是使用 math.IsNaN()
}
二、随机数
Go 语言中处理随机数主要涉及两个核心包:
- math/rand:用于生成伪随机数。它速度快,适用于大多数非密码学安全的场景,如游戏、模拟、抽样等。
- crypto/rand:用于生成密码学安全的随机数。它速度较慢,但随机性更高,不可预测,适用于生成密钥、盐值、会话令牌等安全敏感的场景。
2.1 math/rand 包详解
math/rand 是我们日常开发中最常用的随机数包。让我们从最基础的例子开始。
案例:模拟掷骰子
package mainimport ("fmt""math/rand""time"
)func main() {// 为了兼容性或明确性,仍然可以手动播种rand.Seed(time.Now().UnixNano())// 掷一个6面的骰子,结果范围是 [1, 6]// rand.Intn(6) 生成 [0, 5],所以我们 +1diceRoll := rand.Intn(6) + 1fmt.Printf("你掷出了: %d\n", diceRoll)// 模拟掷10次骰子fmt.Println("连续掷10次骰子:")for i := 0; i < 10; i++ {fmt.Print(rand.Intn(6)+1, " ")}fmt.Println()
}
案例:随机抽奖
package mainimport ("fmt""math/rand""time"
)func main() {rand.Seed(time.Now().UnixNano())prizes := []string{"一等奖", "二等奖", "三等奖", "谢谢参与", "优惠券"}// 随机选择一个索引,范围是 [0, len(prizes))randomIndex := rand.Intn(len(prizes))winner := prizes[randomIndex]fmt.Printf("恭喜你,抽中了: %s\n", winner)
}
2.2 crypto/rand 包详解
当你需要生成无法被预测的随机数时,crypto/rand 是唯一的选择。它的用法与 math/rand 完全不同,它不提供像 Intn() 或 Float64() 这样的便捷函数,而是专注于生成原始的随机字节流。
案例:生成一个16字节(128位)的随机密钥
package mainimport ("crypto/rand""fmt"
)func main() {key := make([]byte, 16) // 创建一个16字节的切片// 从 crypto/rand 读取随机数填充到 key 切片中// Read 是一个阻塞操作,它会等待系统收集到足够的熵n, err := rand.Read(key)if err != nil {// 在某些系统上(如某些 WebAssembly 环境或无熵源的容器),可能会失败fmt.Println("生成随机数失败:", err)return}fmt.Printf("成功读取了 %d 字节的随机数\n", n)fmt.Printf("生成的随机密钥 (十六进制): %x\n", key)
}