Concepts / Generating Random Floating-Point Numbers with random.random()

Generating Random Floating-Point Numbers with random.random()

random.randint(low, high) generates a random integer within an inclusive range, including both the low and high endpoints.

  • Programming
Interactive lab

Try it: random.seed and the Mersenne Twister

Python's random module is a deterministic generator: random.seed(n) sets the Mersenne Twister's internal state, and every random(), randint(), choice() and shuffle() consumes 32-bit outputs from it in order — so the same seed replays the same sequence and a different seed gives a different one.

How it works

  1. random.seed(n) splits |n| into 32-bit words and runs MT19937's init_by_array, filling the 624-word state.
  2. Each 32-bit output takes the next state word and tempers it; after 624 words the whole state is regenerated (the twist).
  3. random() joins two outputs: with , , a float in [0.0, 1.0).
  4. randint(a, b) = a + _randbelow(b − a + 1); choice(seq) = seq[_randbelow(len(seq))]; _randbelow(n) keeps the top n.bit_length() bits and rejects values ≥ n.
  5. shuffle walks i from the end down to 1 and swaps x[i] with x[_randbelow(i + 1)].
  6. Replaying the program with a second seed shows where the sequences agree or first diverge.

Default run (11 steps): A program of 5 random-module calls, run after random.seed(42). … Replay with random.seed(42): every result is identical — same seed, same state, same sequence.

Simplified: Seeds are non-negative integers below (one key word), at most 8 calls, randint bounds within ±1000, and lists of up to 8 simple items. Only mt[0..3] and mt[623] of the 624-word state vector are shown (abbreviated). Everything else follows CPython 3.12's random module exactly.

Educational simulation

Loading the simulation…

A Number That Changes Each Call

Random-number functions are useful when a program must make a choice that is not fixed in advance. Python's random.random() produces a pseudorandom floating-point number between 0.0 and 1.0. The value may look unpredictable, but each call produces the next value determined by the random module's internal state.

What do you think happens?

If a loop calls random.random() three times, will all three calls return the same value?

Reveal answer
Answer:

No. Each call returns a new pseudorandom float and advances the random module's internal state.

The values appear unpredictable, but they belong to a deterministic sequence determined by the module's state. Without resetting the seed, another run can produce a different sequence.

The Output Interval

Definition

random.random() generates a pseudorandom float in the range [0.0, 1.0). The square bracket means that 0.0 is included. The parenthesis means that 1.0 is excluded.

A result can be exactly 0.0, but it will never be exactly 1.0. Other results lie between those boundaries. This half-open interval is useful when scaling the result: multiplying by 100 produces a float in [0.0, 100.0), while multiplying by 6 and converting to an integer can produce one of 0, 1, 2, 3, 4, or 5.

0.0includedBetween 0.0 and 1.0possible float values1.0excludedincluded interval continuesstops before
Where can random.random() place a result, and why is 0.0 possible while 1.0 is not?
python

The printed value is not fixed in advance. A valid run might display a value such as 0.37, but the exact result depends on the random module's internal state. The important check is that the result is at least 0.0 and less than 1.0.

Following the Internal State

Every call to random.random() is a separate event. The random module maintains internal state, and that state changes as calls are made. The next call uses the new state to determine the next pseudorandom float. The function does not need to remember the previous value as a variable; the module's internal state controls what comes next.

Code walkthrough
import random

for step in range(3):
    value = random.random()
    print(value)
  1. 1
    The random module is imported before random.random() is called.
  2. 2
    The loop begins its first iteration and obtains one pseudorandom float.
  3. 3
    The second iteration makes another call, which uses the random module's updated internal state.
  4. 4
    The third iteration makes a third call and prints another value from the sequence.
Iteration 1random.random() produces afloatIteration 2next float from updatedstateIteration 3next float from updatedstateinternal state advancesinternal state advances
What value is produced on each loop iteration, and how does the pseudorandom sequence advance?

When Expected Output Diverges

When actual random output differs from a sequence you expected, the divergence occurs at the call to random.random() itself. The cause can be an unset seed, a different seed, or a different number of random calls made before the code under inspection runs. The surrounding loop may be working correctly; the pseudorandom value entering the loop's output is what differs.

Debugging

Suppose you expected five values: [0.1, 0.2, 0.3, 0.4, 0.5]. Instead, the program produced [0.7, 0.8, 0.9, 0.6, 0.5]. The first divergence is the first call to random.random(): the actual first value is 0.7 rather than the expected 0.1. This does not by itself indicate a bug in the loop. To reproduce the earlier sequence, use the same seed that produced it.

0.1expected first value0.7actual first value0.2expected second value0.8actual second valuefirst call differslater values follow another sequence
At which step does the actual pseudorandom sequence first differ from the expected sequence?

Integer Boundaries with randint()

random.randint(low, high) generates a random integer in an inclusive range. Both endpoints are possible. For random.randint(5, 10), the possible results are 5, 6, 7, 8, 9, and 10, and each has a 1-in-6 chance on any given call.

5included by randint(5, 10)5included by range(5, 10)10included by randint(5, 10)10excluded by range(5, 10)both endpoints includedstops before upper endpoint
Which endpoint values can each construct produce?
ExpressionPossible endpoint behavior
random.randint(5, 10)5 and 10 can both be returned
range(5, 10)5 is included and 10 is excluded

The upper boundary is handled differently by randint() and range().

python

The same inclusive-boundary idea can represent a real-world integer choice. For example, random.randint(1, 100) can select an integer from 1 through 100, including both 1 and 100. Use randint() when the endpoints belong to the requested set of possible integer results.

1low endpoint included2 through 99intermediate integers100high endpoint includednext possible integersends at included high endpoint
How does a requested range map to the possible outputs of randint(1, 100)?

Mistakes in Random Calls

  • Common mistake

    Forgetting to import the random module

    What it looks like
    number = random.randint(1, 10)
    Why it fails

    The name random is not available until the module has been imported.

    Do this instead

    Write import random before calling random.randint() or random.random().

  • Common mistake

    Treating randint() like range() and excluding the high value

    What it looks like
    random.randint(1, 6) is assumed to produce only 1 through 5.
    Why it fails

    randint() includes both its low and high endpoints.

    Do this instead
    Expect 1, 2, 3, 4, 5, or 6 from random.randint(1, 6).
  • Common mistake

    Passing the boundaries in the wrong order

    What it looks like
    random.randint(10, 5)
    Why it fails

    The call does not express the intended low-to-high range.

    Do this instead

    Place the lower integer first and the higher integer second.

  • Common mistake

    Passing non-integer arguments to randint()

    What it looks like
    random.randint(1.0, 6.0)
    Why it fails

    randint() requires integer parameters.

    Do this instead
    Pass integer values such as random.randint(1, 6).
  • Common mistake

    Expecting random.random() to return exactly 1.0

    What it looks like
    if random.random() == 1.0:
    Why it fails

    The upper boundary of random.random() is exclusive.

    Do this instead

    Treat the result as a value in [0.0, 1.0), where 0.0 is included and 1.0 is not.

Before
import random

choice = random.randint(1, 5)

The possible results are 1, 2, 3, 4, and 5.

After
import random

choice = random.randint(1, 6)

The possible results are 1, 2, 3, 4, 5, and 6.

randint(1, 5)possible values 1 through 5randint(1, 6)possible values 1 through 6high endpoint changes from 5 to 6
What changes when the high argument is corrected to include the intended endpoint?

Practice and Recap

EASY

Write a short Python program that imports random, calls random.random() four times in a loop, and prints each result. Before running it, write down the range every printed value must satisfy. Then explain why your exact four values may differ if you run the program again.

Hints
  • Use for and range() to repeat the call four times.
  • Each result must be at least 0.0 and less than 1.0.
  • The internal state of the random module advances after each call.
  • Use the same seed before both runs if you need identical sequences.
  1. 1
    random.random() returns a pseudorandom float in [0.0, 1.0): 0.0 is possible, while 1.0 is excluded. Each call advances the random module's internal state and returns the next value in its pseudorandom sequence. Without resetting the seed, separate runs can produce different sequences; using the same seed makes sequences reproducible. When debugging, locate the first call whose result differs from your expectation. For integer choices, random.randint(low, high) includes both endpoints, unlike range(), which excludes its upper endpoint.

Key Takeaways

  • random.random() generates a pseudorandom float in the half-open interval [0.0, 1.0).
  • The lower boundary 0.0 is included, while the upper boundary 1.0 is excluded.
  • Repeated calls advance the random module's internal state and produce successive values from a pseudorandom sequence.
  • Setting the same seed before repeated runs makes the sequence reproducible.
  • random.randint(low, high) includes both integer endpoints, unlike range(), and requires integer arguments.

Related concepts

More concepts starting with G