ID:1960730
 
Resolved
rand() could sometimes return 1, and rand(x,y) could sometimes return y+1, due to floating point rounding errors.
BYOND Version:509.1305
Operating System:Linux
Web Browser:Chrome 46.0.2490.71
Applies to:Dream Daemon
Status: Resolved (509.1307)

This issue has been resolved.
Descriptive Problem Summary: Very rarely (single-digit counts in 50M calls), rand(x,y) will return y+1.

Numbered Steps to Reproduce Problem:
- Call rand(x,y) a large amount of times (~50M) in a loop
- Watch the return value occasionally be y+1

Code Snippet (if applicable) to Reproduce Problem:
/world/New()
var/list/count[6]
for(var/k = 1 to 10)
for(var/i = 1 to 5000000)
count[rand(1,5)]++
// count[round(rand()*5 + 1)]++

for(var/i = 1 to count.len)
world.log << "[i]=[count[i]]"

(swap the commented-out line with the one above it for how I'm guessing rand(x,y) is implemented internally - it shows identical behaviour)

Example output:

1=1.00025e+07
2=1.00027e+07
3=9.99989e+06
4=9.99671e+06
5=9.99818e+06
6=3


Expected Results: rand(x,y) would return an integer between x and y inclusive with roughly equal probability, as specified by the reference

Actual Results: rand(x,y) very occasionally returns y+1

Does the problem occur:
Every time? Or how often? Less than once per ten million calls (when rand() returns 1.0)
In other games? N/A
In other user accounts? Unknown
On other computers? Unknown

Did the problem NOT occur in any earlier versions? If so, what was the last version that worked? Unknown

Workarounds: Check the return from rand(x,y)?
Good find. I think I know what causes this, and I can fix it.

Interestingly, your numbers coincide exactly with my hunch. The problem comes in because the raw rand() is a 32-bit number divided by 2^32, which works fine for double precision, but for floating point it will round. ANDing the number with 0xFFFFFF00 before the division will solve the problem.
Lummox JR resolved issue with message:
rand() could sometimes return 1, and rand(x,y) could sometimes return y+1, due to floating point rounding errors.
Would this bug have also affected the pick() function? There are a couple places in NEStalgia's code where the use of pick(list) very rarely causes an "index out of bounds" runtime, even though there are checks to make sure that the list actually contains stuff first. It's been driving me crazy trying to figure out what's been going wrong, especially since it doesn't happen on any sort of consistent basis.
In response to SilkWizard
SilkWizard wrote:
Would this bug have also affected the pick() function?

Omg! Seconding this: http://www.byond.com/forum/?post=1623236
Yep, this would have impacted pick().
If pick is simply implemented according to something like the following pseudocode

proc/pick(list/list)
return list[rand(1, list.len)]


It most certainly would cause it. The question is whether it uses the rand() function.
It uses the same internal function, yes.
fun fact, Tom or Dan implemented pick() (before it was native) in one of their project like this:
proc/pick() {
return args[rand(1,args.len)]
}


back in like, 1998
clearly no support for lists yet
pick() with a list was my idea IIRC. I'm not sure if I was the one who implemented it or if it was something Dan threw in.