Math Operations and Random Numbers in Go
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.
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