Random: NextNumber() should never return zero

System Information:

  • Windows 7 SP1
  • Pentium Dual-Core E6500

Reproduction: Simply run this code:
local r = Random.new(4218675221687534) for i=1,178 do r:NextNumber() end print(r:NextNumber())

Actual Result: 0

Impact: While this has an extremely low chance of happening under a non-seeded case, when it does happen, experiences will operate in unexpected ways. For example, code such as 1/r:NextNumber() to display the RNG value will return Infinity and depending on the code used to select the awarded item, can return nil and error later on, or script timeout.

Developer’s Workaround: Check for zero return value for every place that NextNumber is called and set it to 0.5. It’s very tedious to put this.

Expected behavior

It should print any value except zero.

11 Likes

I don’t think its a bug the function doesn’t say anything about the number not being 0

9 Likes

The documentation for math.random states that the function can return exactly zero in the documentation, but, the documentation for NextNumber does not.

image

I have a lot of scripts to update if this isn’t a bug. In a incremental game I have code that boosts a currency by 1 + -math.log10(r:NextNumber()). If it returned 0, then it would compute to inf.

5 Likes

The documentation of NextNumber does state it can return exactly zero. At least assuming that common notation has been used, [0, 1] means a number in the range from 0 to 1, inclusive of both 0 and 1. Mind you that’s still wrong if 1 can’t happen, but it is documented that 0 can already.

I don’t think a specific use case of the function not working quite properly is a compelling enough case to change the behavior altogether. In fact, this behavior is expected of just about every single language that implements a way to get a random float.

14 Likes

like Autterfly said this isn’t a bug
[0,1] means from 0 and 1 and including 0 and 1
]0,1] means from 0 to 1 but excluding 0
[0,1[ means from 0 to 1 but excluding 1
]0,1[ means from 0 to one but excluding both

11 Likes

Isn’t such includes/excludes use [X;Y] for inclusion and (X;Y) for exclusion?

8 Likes

yes that is right
but you can also make a half-opened intervals where 1 number of both numbers is exluded/included

4 Likes
local function safeNextNumber(random)
    while true do
        local result = random:NextNumber()
        if result == result and result ~= math.huge then
            return result
        end
    end
end

Also, it’s basically impossible to get zero.

image

I ran the function billions of times, and it never returned zero. Unsure why your code does though, mind sharing how you got that seed and the number 178? Point is, in a real scenario, it’s not going to happen. Besides, if you get 1/INF in your RNG game, wouldn’t that mean you just get the rarest item (since you didn’t code a rarer one)? Is it that big of an issue to you that it could return zero?

5 Likes

its possiple to get zero but the chance is very rare and its based on the seed
i got it after 178 tries using the 4218675221687534 seed

--!strict
local r = Random.new(4218675221687534)


for i=0, 1e8 do
	local n = r:NextNumber()
	if n == 0 then
		print("Printed Zero", " | Took " .. i .. " Tries")
	end
end

print("LoopDone")

{96FD4077-2999-49B9-9701-B0D7F3C72877}
but when i try with a different seed for example local r = Random.new(20) then I didn’t get 0 in 1e8 loops

3 Likes

Assuming this isn’t a bug, it won’t be too hard to update all your code.

You can just create a single module script that handles it for you;

function NextNumber(r: Random): number
	local n = r:NextNumber();
	return n > 0 and n or NextNumber(r);
end

return NextNumber

Then just CTRL + F and replace all the r:NextNumber() to use your custom NextNumber(r) function.

2 Likes

i got them from the exact script that i sent and i got the seed from the op topic

Script
--!strict
local r = Random.new(4218675221687534)


for i=0, 1e8 do
	local n = r:NextNumber()
	if n == 0 then
		print("Printed Zero", " | Took " .. i .. " Tries")
	end
end

print("LoopDone")

that depends on the rng system but for weight rng systems there are systems where the higher the weight the rarer the item and there are systems where the lower the weight the rarer the item

no because I wouldn’t divide 1 by the random number and that would mean that I got the rarest item

1 Like

As explained in the discussion for this issue, the reported behavior is intentional and we don’t currently have plans to update it.

Because the space of possible double precision floating-point numbers in large, seeing a specific one and not only specifically 0 can be pretty rare.

1 Like

Can we update this documentation to include the correct range notation?

What do you see as incorrect in the documentation range?

Based upon this image, it should be [0, 1) to indicate inclusive 0, but exclusive 1.

image

This image is for math.random and not Random:NextNumber.
It is actually quite likely that math.random is incorrect and might generate a 1 just like Random:NextNumber.

1 Like