Skip to main content
  1. Tutorials/

Generate Random Numbers in Go

··4 mins

Random numbers are useful for games, simulations, shuffling, sampling, and test data. In Go 1.22+, the recommended package for non-secure randomness is math/rand/v2, which provides modern generators and a clearer API. For anything security-sensitive (tokens, passwords, keys), use crypto/rand instead. 🚀

What Is Random Number Generation in Go? #

math/rand/v2 uses pseudo-random number generators (PRNGs). PRNGs are fast and repeatable, but they are deterministic once the internal state is known. That makes them great for simulations and games, and a bad fit for security. If you start a generator with the same seed, you will get the same sequence again, which is handy for tests.

When to Use Random Numbers #

  • Simulations, games, shuffling, sampling: use math/rand/v2
  • Security-sensitive values (tokens, passwords, keys): use crypto/rand
  • Repeatable tests: use a fixed seed with a custom *rand.Rand

How to Generate Random Numbers in Go (math/rand/v2) #

Full working example using math/rand/v2:

package main

import (
    "fmt"
    "math/rand/v2"
)

func randIntRange(min, max int) int {
    if max <= min {
        panic("max must be greater than min")
    }
    return min + rand.IntN(max-min)
}

func randFloatRange(min, max float64) float64 {
    if max <= min {
        panic("max must be greater than min")
    }
    return min + rand.Float64()*(max-min)
}

func main() {
    fmt.Println("any int:", rand.Int())
    fmt.Printf("any float64: %.6f\n", rand.Float64())

    fmt.Println("int in [5, 10):", randIntRange(5, 10))
    fmt.Printf("float64 in [2.4, 3.2): %.6f\n", randFloatRange(2.4, 3.2))
}

Example output:

any int: 5696350091330895579
any float64: 0.764513
int in [5, 10): 6
float64 in [2.4, 3.2): 3.142109

How it works:

  • rand.Int returns a non-negative int.
  • rand.Float64 returns a float in [0.0, 1.0).
  • Int range: min + rand.IntN(max-min) produces [min, max) using rand.IntN.
  • Float range: min + rand.Float64()*(max-min) produces [min, max) using rand.Float64.

Modern Go Features (Go 1.22+): math/rand/v2 #

The new math/rand/v2 package is recommended for new code because:

  • It includes modern generators like PCG and ChaCha8 (still not cryptographically secure).
  • It adds clearer range helpers such as IntN, Int64N, UintN, and the generic N.
  • It removes the old global seeding pattern; if you want repeatable output, create your own generator with rand.New.

PCG stands for Permuted Congruential Generator. It is a fast PRNG family with good statistical quality for simulations, games, and tests.

Reproducible random numbers for tests #

package main

import (
    "fmt"
    "math/rand/v2"
)

func main() {
    r := rand.New(rand.NewPCG(1, 2))
    fmt.Println(r.IntN(100))
    fmt.Println(r.IntN(100))
}

Use a fixed seed for tests so results stay stable. This example builds a local generator with rand.New and rand.NewPCG, which returns a *rand.Rand you can pass around.

Secure Random Numbers with crypto/rand #

For tokens, passwords, and security-sensitive data, use crypto/rand. It reads from the operating system’s cryptographically secure random source.

package main

import (
    "crypto/rand"
    "fmt"
    "math/big"
)

func secureIntRange(min, max int64) (int64, error) {
    if max <= min {
        return 0, fmt.Errorf("max must be greater than min")
    }

    n, err := rand.Int(rand.Reader, big.NewInt(max-min))
    if err != nil {
        return 0, err
    }

    return min + n.Int64(), nil
}

func main() {
    v, err := secureIntRange(1000, 10000)
    if err != nil {
        panic(err)
    }
    fmt.Println("secure int in [1000, 10000):", v)
}

Like math/rand/v2, crypto/rand.Int treats the upper bound as exclusive, so the range is [min, max). This uses crypto/rand.Int with rand.Reader and big.NewInt to set the upper bound.

Gotchas #

  • math/rand/v2 is not for secrets; use crypto/rand for tokens and passwords.
  • Range helpers like IntN expect max > min; otherwise you will panic on IntN(0).
  • A *rand.Rand is not safe for concurrent use without synchronization.

Best Practices #

  • Validate ranges so max > min and avoid passing 0 to IntN.
  • Use math/rand/v2 for simulations and tests, not for secrets.
  • Create a local *rand.Rand with rand.NewPCG when you need reproducible output.
  • The top-level functions are concurrency-safe, but a *rand.Rand needs synchronization if shared.

Tested with Go 1.25+ | Last verified: December 2025

Happy coding! 🎉