Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-11447] Infinite loop in RandomNumberGenerator using drand48 #53848

Closed
swift-ci opened this issue Sep 10, 2019 · 4 comments
Closed

[SR-11447] Infinite loop in RandomNumberGenerator using drand48 #53848

swift-ci opened this issue Sep 10, 2019 · 4 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. standard library Area: Standard library umbrella

Comments

@swift-ci
Copy link
Collaborator

Previous ID SR-11447
Radar None
Original Reporter dbplunkett (JIRA User)
Type Bug
Status Closed
Resolution Invalid
Environment

Xcode 10.3 (10G8)
Swift 5.0.1

Additional Detail from JIRA
Votes 0
Component/s Standard Library
Labels Bug
Assignee None
Priority Medium

md5: 71caa2ca8b9939510bf7e4ee0645d128

Issue Description:

A simple implementation of RandomNumberGenerator using drand48 results in infinite calls to next() when used to find a random 8- or 16-bit Int or UInt.

This issue does not occur with other simple implementations, e.g. using arc4random, nor if it's used to find a random 32- or 64-bit Int or UInt.

The range passed to the random method doesn't seem to have any impact.

Uncomment lines below to see an example.

struct RNG: RandomNumberGenerator {

    //  is infinitely called in the Int8, Int16, UInt8, and UInt16 examples
    func next() -> UInt64 {
        return .init(drand48() * .init(UInt64.max))
    }

    //  works with all examples
//    func next() -> UInt64 {
//        return .init(Double(arc4random()) / .init(UInt32.max) * .init(UInt64.max))
//    }
}

var rng = RNG()

//Int64.random(in: 1...10, using: &rng)
//Int32.random(in: 1...10, using: &rng)
//Int16.random(in: 1...10, using: &rng)
//Int8.random(in: 1...10, using: &rng)

//UInt64.random(in: 1...10, using: &rng)
//UInt32.random(in: 1...10, using: &rng)
//UInt16.random(in: 1...10, using: &rng)
//UInt8.random(in: 1...10, using: &rng)
@belkadan
Copy link
Contributor

@stephentyrone, any ideas?

@stephentyrone
Copy link
Member

.init(drand48() * .init(UInt64.max)) will always result in a UInt64 whose low-order 16 bits are all zero. The .random(in) method first creates a UInt(8|16) using init(truncatingIfNeeded), which always results in all-zeros. That implementation could be somewhat more sophisticated to avoid wasting calls to next(), but even so this is an inadequate base RNG, and some failure is expected to occur.

TL;DR: don't use drand48() like this. It does not satisfy the requirements of Swift's RandomNumberGenerator protocol.

@swift-ci
Copy link
Collaborator Author

Comment by David Plunkett (JIRA)

Ahh, I see. That implementation is a surprise - however, I suppose it's better that my bad randomness caused an obvious failure, rather than simply producing a working but subtly non-random instance of a RandomNumberGenerator.

Thanks.

@stephentyrone
Copy link
Member

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. standard library Area: Standard library umbrella
Projects
None yet
Development

No branches or pull requests

3 participants