Go provides comprehensive support for mathematical operations through the math package and random number generation through the rand and crypto/rand packages. This guide covers everything you need for numerical computing in Go. For more context, see Go Installation Guide, Go Ecosystem Overview, Go Best Practices.
The Math Package
Basic Mathematical Functions
package main
import (
"fmt"
"math"
)
func main() {
// Absolute value
fmt.Println("Abs(-5):", math.Abs(-5))
// Square root
fmt.Println("Sqrt(16):", math.Sqrt(16))
// Power
fmt.Println("Pow(2, 8):", math.Pow(2, 8))
// Ceiling and floor
fmt.Println("Ceil(4.3):", math.Ceil(4.3))
fmt.Println("Floor(4.7):", math.Floor(4.7))
// Rounding
fmt.Println("Round(4.5):", math.Round(4.5))
fmt.Println("Trunc(4.9):", math.Trunc(4.9))
// Minimum and maximum
fmt.Println("Min(5, 3):", math.Min(5, 3))
fmt.Println("Max(5, 3):", math.Max(5, 3))
}
Trigonometric Functions
package main
import (
"fmt"
"math"
)
func main() {
// Sine, cosine, tangent (in radians)
angle := math.Pi / 4 // 45 degrees
fmt.Printf("Sin(π/4): %.4f\n", math.Sin(angle))
fmt.Printf("Cos(π/4): %.4f\n", math.Cos(angle))
fmt.Printf("Tan(π/4): %.4f\n", math.Tan(angle))
// Inverse trigonometric functions
fmt.Printf("Asin(0.707): %.4f\n", math.Asin(0.707))
fmt.Printf("Acos(0.707): %.4f\n", math.Acos(0.707))
fmt.Printf("Atan(1): %.4f\n", math.Atan(1))
// Atan2 for angle calculation
fmt.Printf("Atan2(1, 1): %.4f\n", math.Atan2(1, 1))
// Convert degrees to radians
degrees := 45.0
radians := degrees * math.Pi / 180
fmt.Printf("%f degrees = %f radians\n", degrees, radians)
}
Logarithmic and Exponential Functions
package main
import (
"fmt"
"math"
)
func main() {
// Natural logarithm
fmt.Printf("Log(2.718): %.4f\n", math.Log(math.E))
// Base 10 logarithm
fmt.Printf("Log10(100): %.4f\n", math.Log10(100))
// Base 2 logarithm
fmt.Printf("Log2(8): %.4f\n", math.Log2(8))
// Exponential (e^x)
fmt.Printf("Exp(1): %.4f\n", math.Exp(1))
// Power with base e
fmt.Printf("Exp2(3): %.4f\n", math.Exp2(3))
// Hyperbolic functions
fmt.Printf("Sinh(1): %.4f\n", math.Sinh(1))
fmt.Printf("Cosh(1): %.4f\n", math.Cosh(1))
fmt.Printf("Tanh(1): %.4f\n", math.Tanh(1))
}
Special Values and Constants
package main
import (
"fmt"
"math"
)
func main() {
// Mathematical constants
fmt.Println("Pi:", math.Pi)
fmt.Println("E:", math.E)
fmt.Println("Phi (Golden Ratio):", math.Phi)
fmt.Println("Sqrt2:", math.Sqrt2)
fmt.Println("SqrtE:", math.SqrtE)
fmt.Println("SqrtPi:", math.SqrtPi)
fmt.Println("SqrtPhi:", math.SqrtPhi)
// Special floating point values
fmt.Println("Inf:", math.Inf(1))
fmt.Println("-Inf:", math.Inf(-1))
fmt.Println("NaN:", math.NaN())
// Check for special values
fmt.Println("IsInf(Inf(1)):", math.IsInf(math.Inf(1), 1))
fmt.Println("IsNaN(NaN()):", math.IsNaN(math.NaN()))
fmt.Println("IsFinite(5.0):", math.IsFinite(5.0))
}
Random Number Generation
Using the rand Package
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
// Seed the random number generator
rand.Seed(time.Now().UnixNano())
// Generate random integers
fmt.Println("Random int:", rand.Int())
fmt.Println("Random int63:", rand.Int63())
// Generate random integers in range [0, n)
fmt.Println("Random int in [0, 100):", rand.Intn(100))
// Generate random float64 in [0.0, 1.0)
fmt.Println("Random float64:", rand.Float64())
// Generate random float64 in [0.0, n)
fmt.Println("Random float64 in [0, 100):", rand.Float64()*100)
}
Seeding for Reproducibility
package main
import (
"fmt"
"math/rand"
)
func main() {
// Seed with fixed value for reproducible results
rand.Seed(42)
fmt.Println("First run:")
for i := 0; i < 3; i++ {
fmt.Println(rand.Intn(100))
}
// Reseed with same value
rand.Seed(42)
fmt.Println("\nSecond run (same seed):")
for i := 0; i < 3; i++ {
fmt.Println(rand.Intn(100))
}
}
Random Selections
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano())
// Shuffle a slice
numbers := []int{1, 2, 3, 4, 5}
rand.Shuffle(len(numbers), func(i, j int) {
numbers[i], numbers[j] = numbers[j], numbers[i]
})
fmt.Println("Shuffled:", numbers)
// Random selection from slice
choices := []string{"apple", "banana", "cherry", "date"}
fmt.Println("Random choice:", choices[rand.Intn(len(choices))])
// Generate random permutation
perm := rand.Perm(5)
fmt.Println("Random permutation:", perm)
}
Cryptographically Secure Random Numbers
package main
import (
"crypto/rand"
"fmt"
"log"
"math/big"
)
func main() {
// Generate cryptographically secure random bytes
bytes := make([]byte, 16)
_, err := rand.Read(bytes)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Random bytes: %x\n", bytes)
// Generate random big integer
max := big.NewInt(100)
randomBig, err := rand.Int(rand.Reader, max)
if err != nil {
log.Fatal(err)
}
fmt.Println("Random big int:", randomBig)
// Generate random number in range
randomInRange, err := rand.Int(rand.Reader, big.NewInt(100))
if err != nil {
log.Fatal(err)
}
fmt.Println("Random in [0, 100):", randomInRange)
}
Practical Examples
Simulating Dice Rolls
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano())
// Roll a die 10 times
fmt.Println("Rolling a die 10 times:")
for i := 0; i < 10; i++ {
roll := rand.Intn(6) + 1 // 1-6
fmt.Printf("Roll %d: %d\n", i+1, roll)
}
// Simulate multiple dice rolls
fmt.Println("\nRolling two dice 5 times:")
for i := 0; i < 5; i++ {
die1 := rand.Intn(6) + 1
die2 := rand.Intn(6) + 1
fmt.Printf("Roll %d: %d + %d = %d\n", i+1, die1, die2, die1+die2)
}
}
Monte Carlo Simulation
package main
import (
"fmt"
"math"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano())
// Estimate Pi using Monte Carlo method
numSamples := 1000000
pointsInCircle := 0
for i := 0; i < numSamples; i++ {
x := rand.Float64()
y := rand.Float64()
distance := math.Sqrt(x*x + y*y)
if distance <= 1.0 {
pointsInCircle++
}
}
// Pi ≈ 4 * (points in circle / total points)
estimatedPi := 4.0 * float64(pointsInCircle) / float64(numSamples)
fmt.Printf("Estimated Pi: %.6f\n", estimatedPi)
fmt.Printf("Actual Pi: %.6f\n", math.Pi)
fmt.Printf("Error: %.6f\n", math.Abs(estimatedPi-math.Pi))
}
Random String Generation
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano())
// Generate random string
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
length := 10
randomString := make([]byte, length)
for i := range randomString {
randomString[i] = charset[rand.Intn(len(charset))]
}
fmt.Println("Random string:", string(randomString))
// Generate multiple random strings
fmt.Println("\nRandom strings:")
for i := 0; i < 5; i++ {
str := make([]byte, 8)
for j := range str {
str[j] = charset[rand.Intn(len(charset))]
}
fmt.Println(string(str))
}
}
Weighted Random Selection
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano())
// Weighted selection
type Option struct {
name string
weight int
}
options := []Option{
{"common", 70},
{"uncommon", 25},
{"rare", 4},
{"legendary", 1},
}
// Calculate total weight
totalWeight := 0
for _, opt := range options {
totalWeight += opt.weight
}
// Simulate 1000 selections
counts := make(map[string]int)
for i := 0; i < 1000; i++ {
r := rand.Intn(totalWeight)
cumulative := 0
for _, opt := range options {
cumulative += opt.weight
if r < cumulative {
counts[opt.name]++
break
}
}
}
fmt.Println("Distribution from 1000 selections:")
for _, opt := range options {
fmt.Printf("%s: %d (%.1f%%)\n", opt.name, counts[opt.name], float64(counts[opt.name])/10)
}
}
Best Practices
✅ Good Practices
// Use crypto/rand for security-sensitive operations
func generateSecureToken() ([]byte, error) {
token := make([]byte, 32)
_, err := rand.Read(token)
return token, err
}
// Seed once at program start
func init() {
rand.Seed(time.Now().UnixNano())
}
// Use math constants instead of hardcoding
func calculateCircleArea(radius float64) float64 {
return math.Pi * radius * radius
}
// Handle special floating point values
func safeLog(x float64) float64 {
if x <= 0 {
return math.NaN()
}
return math.Log(x)
}
❌ Anti-Patterns
// Don't seed multiple times
func badRandom() {
rand.Seed(time.Now().UnixNano())
rand.Seed(time.Now().UnixNano())
// Reduces randomness
}
// Don't use math/rand for security
func badToken() {
token := rand.Int63()
// Not cryptographically secure
}
// Don't ignore special values
func badCalculation(x float64) float64 {
return math.Log(x) // Panics if x <= 0
}
// Don't hardcode mathematical constants
func badArea(r float64) float64 {
return 3.14159 * r * r // Less precise than math.Pi
}
Common Pitfalls
Seeding Issues
// ❌ Wrong: Seeding in loop reduces randomness
for i := 0; i < 10; i++ {
rand.Seed(time.Now().UnixNano())
fmt.Println(rand.Intn(100))
}
// ✅ Correct: Seed once at startup
func init() {
rand.Seed(time.Now().UnixNano())
}
Floating Point Precision
// ❌ Wrong: Direct comparison of floats
if math.Sqrt(2)*math.Sqrt(2) == 2 {
fmt.Println("Equal")
}
// ✅ Correct: Use epsilon for comparison
epsilon := 1e-9
if math.Abs(math.Sqrt(2)*math.Sqrt(2)-2) < epsilon {
fmt.Println("Equal")
}
Range Errors
// ❌ Wrong: Incorrect range calculation
randomInRange := rand.Intn(100) // [0, 100)
// ✅ Correct: Adjust for desired range
randomInRange := rand.Intn(100) + 1 // [1, 100]
Resources
- Go math Package Documentation
- Go math/rand Package Documentation
- Go crypto/rand Package Documentation
- IEEE 754 Floating Point Standard
- Monte Carlo Methods
Summary
Go’s math and random packages provide powerful tools for numerical computing:
- Use
mathpackage for mathematical functions and constants - Use
math/randfor general-purpose random numbers - Use
crypto/randfor security-sensitive operations - Seed once at program startup
- Handle special floating point values properly
- Use epsilon for floating point comparisons
With these tools, you can implement sophisticated numerical algorithms and simulations in Go.
Comments