You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Swift中实现xoshiro256+ PRNG并生成指定范围随机数的问题

Fixing Your xoshiro256+ Implementation in Swift

Hey there! Let's tackle the issues you're facing with your xoshiro256+ PRNG implementation and get it working smoothly.

1. The Root Cause of Your Crash: Unsigned Overflow

Your crash after 6 calls is almost certainly due to unsigned integer overflow. In C, unsigned integer overflow is well-defined (it wraps around modulo 2^64), but Swift defaults to trapping on overflow for arithmetic operations.

In your next() function, the line:

let result_plus = s[0] + s[3]

will trigger a runtime error when s[0] + s[3] exceeds UInt64.max. To match the C behavior, you need to use Swift's overflow addition operator &+:

let result_plus = s[0] &+ s[3]

This lets the value wrap around as intended by the xoshiro256+ algorithm.

2. Converting to a 0-1 Double

To convert the 64-bit unsigned output to a Double in the [0, 1) range, you have two solid options:

Option A: Direct Conversion (Simpler)

Treat the UInt64 result as a value between 0 and 2^64 - 1, then divide by 2^64. Since UInt64.max is 2^64 - 1, we add 1.0 to get the full range:

func nextDouble() -> Double {
    let raw = next()
    return Double(raw) / (Double(UInt64.max) + 1.0)
}

Option B: Precision-Optimized Conversion (Better for Double)

Double only has 53 bits of mantissa precision, so using all 64 bits of the PRNG output is wasteful. We can shift the result right by 11 bits to align with Double's precision, then divide by 2^53:

func nextDouble() -> Double {
    let raw = next()
    let shifted = raw >> 11 // Keep the top 53 bits
    return Double(shifted) / Double(1 << 53)
}

This gives you the maximum possible precision for a Double, which is ideal for most use cases.

3. Generating Random Numbers in a Custom Range

Once you have your nextDouble() function, generating numbers in a range [min, max) is straightforward:

func next(in range: ClosedRange<Double>) -> Double {
    let min = range.lowerBound
    let max = range.upperBound
    return min + (max - min) * nextDouble()
}

// Or for integer ranges (if needed):
func next(in range: ClosedRange<Int>) -> Int {
    let min = range.lowerBound
    let max = range.upperBound
    let doubleValue = Double(min) + (Double(max - min + 1) * nextDouble())
    return Int(doubleValue)
}

4. Full Working Implementation

Here's the updated code with all fixes and additions:

func rotl(_ x: UInt64, _ k: Int) -> UInt64 {
    return (x << k) | (x >> (64 - k))
}

class Xoshiro256Plus {
    private var s: [UInt64]
    
    // Initialize with a seed vector (replace with proper seeding in production!)
    init(seed: [UInt64]) {
        precondition(seed.count == 4, "Seed must be a 4-element UInt64 array")
        self.s = seed
    }
    
    func next() -> UInt64 {
        let result_plus = s[0] &+ s[3] // Use overflow addition
        let t = s[1] << 17
        
        s[2] ^= s[0]
        s[3] ^= s[1]
        s[1] ^= s[2]
        s[0] ^= s[3]
        
        s[2] ^= t
        s[3] = rotl(s[3], 45)
        
        return result_plus
    }
    
    func nextDouble() -> Double {
        let raw = next()
        let shifted = raw >> 11
        return Double(shifted) / Double(1 << 53)
    }
    
    func next(in range: ClosedRange<Double>) -> Double {
        let min = range.lowerBound
        let max = range.upperBound
        return min + (max - min) * nextDouble()
    }
}

// Usage example:
let prng = Xoshiro256Plus(seed: [1, 2, 3, 4])
for _ in 0..<10 {
    print(prng.nextDouble())
    print(prng.next(in: 5.0...10.0))
}

Note: I wrapped the PRNG in a class to encapsulate the state vector s—this is cleaner than using a global variable, especially if you need multiple independent PRNG instances.

5. Quick Notes

  • Seeding: Your initial seed [1,2,3,4] works for testing, but in production, you should use a cryptographically secure seed (e.g., from platform-specific APIs like SecRandomCopyBytes on Apple devices) to avoid predictable outputs.
  • No Need to Discard Low Bits: The overflow fix is all you need to stop the crash. The precision optimization (shifting right) is optional but recommended for Double conversion.

内容的提问来源于stack exchange,提问作者ANoobSwiftly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:17:33