0到1之间的快速随机浮动

在 Swift 中,我试图得到一个0到1之间的随机浮点数,但是我似乎无法得到类型转换。

func randomCGFloat() -> CGFloat {
return CGFloat(arc4random()) / UINT32_MAX
}

我得到一个“ CGFloat”不能转换为“ UInt8”错误

运行 Xcode 6。

66754 次浏览

Try initializing the divisor as a float as well, a la:

CGFloat(Float(arc4random()) / Float(UINT32_MAX))

What jmduke suggested seems to work in Playground with a small change in the function:

func randomCGFloat() -> Float {
return Float(arc4random()) /  Float(UInt32.max)
}

and the reason why from the swift doc and as noted by drewag: type conversion must be explicit, the example in the doc is:

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi equals 3.14159, and is inferred to be of type Double

This is extension for random numbers of Int, Double, Float, CGFloat

Swift 3 & 4 & 5 syntax

import Foundation
import CoreGraphics


// MARK: Int Extension


public extension Int {


/// Returns a random Int point number between 0 and Int.max.
static var random: Int {
return Int.random(n: Int.max)
}


/// Random integer between 0 and n-1.
///
/// - Parameter n:  Interval max
/// - Returns:      Returns a random Int point number between 0 and n max
static func random(n: Int) -> Int {
return Int(arc4random_uniform(UInt32(n)))
}


///  Random integer between min and max
///
/// - Parameters:
///   - min:    Interval minimun
///   - max:    Interval max
/// - Returns:  Returns a random Int point number between 0 and n max
static func random(min: Int, max: Int) -> Int {
return Int.random(n: max - min + 1) + min


}
}


// MARK: Double Extension


public extension Double {


/// Returns a random floating point number between 0.0 and 1.0, inclusive.
static var random: Double {
return Double(arc4random()) / 0xFFFFFFFF
}


/// Random double between 0 and n-1.
///
/// - Parameter n:  Interval max
/// - Returns:      Returns a random double point number between 0 and n max
static func random(min: Double, max: Double) -> Double {
return Double.random * (max - min) + min
}
}


// MARK: Float Extension


public extension Float {


/// Returns a random floating point number between 0.0 and 1.0, inclusive.
static var random: Float {
return Float(arc4random()) / 0xFFFFFFFF
}


/// Random float between 0 and n-1.
///
/// - Parameter n:  Interval max
/// - Returns:      Returns a random float point number between 0 and n max
static func random(min: Float, max: Float) -> Float {
return Float.random * (max - min) + min
}
}


// MARK: CGFloat Extension


public extension CGFloat {


/// Randomly returns either 1.0 or -1.0.
static var randomSign: CGFloat {
return (arc4random_uniform(2) == 0) ? 1.0 : -1.0
}


/// Returns a random floating point number between 0.0 and 1.0, inclusive.
static var random: CGFloat {
return CGFloat(Float.random)
}


/// Random CGFloat between 0 and n-1.
///
/// - Parameter n:  Interval max
/// - Returns:      Returns a random CGFloat point number between 0 and n max
static func random(min: CGFloat, max: CGFloat) -> CGFloat {
return CGFloat.random * (max - min) + min
}
}

Use :

let randomNumDouble  = Double.random(min: 0.00, max: 23.50)
let randomNumInt     = Int.random(min: 56, max: 992)
let randomNumFloat   = Float.random(min: 6.98, max: 923.09)
let randomNumCGFloat = CGFloat.random(min: 6.98, max: 923.09)

Below is an extension on the IntervalType type for doing this:

extension IntervalType {
public func random() -> Bound {
let range = (self.end as! Double) - (self.start as! Double)
let randomValue = (Double(arc4random_uniform(UINT32_MAX)) / Double(UINT32_MAX)) * range + (self.start as! Double)
return randomValue as! Bound
}
}

With this extension, you can use the interval syntax for generating an interval and then getting a random value in that interval:

(0.0...1.0).random()

Addition

If you're looking to do the same for Ints, then you can use the following extension on the CollectionType protocol:

extension CollectionType {
public func random() -> Self._Element {
if let startIndex = self.startIndex as? Int {
let start = UInt32(startIndex)
let end = UInt32(self.endIndex as! Int)
return self[Int(arc4random_uniform(end - start) + start) as! Self.Index]
}
var generator = self.generate()
var count = arc4random_uniform(UInt32(self.count as! Int))
while count > 0 {
generator.next()
count = count - 1
}
return generator.next() as! Self._Element
}
}

Ints don't use the IntervalType. They use Range instead. The benefit of doing this on the CollectionType type is that it's automatically carried over to the Dictionary and Array types.

Examples:

(0...10).random()               // Ex: 6
["A", "B", "C"].random()        // Ex: "B"
["X":1, "Y":2, "Z":3].random()  // Ex: (.0: "Y", .1: 2)

Here framework does a good job at generating random number data in Swift: https://github.com/thellimist/SwiftRandom/blob/master/SwiftRandom/Randoms.swift

public extension Int {
/// SwiftRandom extension
public static func random(lower: Int = 0, _ upper: Int = 100) -> Int {
return lower + Int(arc4random_uniform(UInt32(upper - lower + 1)))
}
}


public extension Double {
/// SwiftRandom extension
public static func random(lower: Double = 0, _ upper: Double = 100) -> Double {
return (Double(arc4random()) / 0xFFFFFFFF) * (upper - lower) + lower
}
}


public extension Float {
/// SwiftRandom extension
public static func random(lower: Float = 0, _ upper: Float = 100) -> Float {
return (Float(arc4random()) / 0xFFFFFFFF) * (upper - lower) + lower
}
}


public extension CGFloat {
/// SwiftRandom extension
public static func random(lower: CGFloat = 0, _ upper: CGFloat = 1) -> CGFloat {
return CGFloat(Float(arc4random()) / Float(UINT32_MAX)) * (upper - lower) + lower
}
}

Updating Sandy Chapman's answer for Swift 3:

extension ClosedRange where Bound : FloatingPoint {
public func random() -> Bound {
let range = self.upperBound - self.lowerBound
let randomValue = (Bound(arc4random_uniform(UINT32_MAX)) / Bound(UINT32_MAX)) * range + self.lowerBound
return randomValue
}
}

Now you can say stuff like (-1.0...1.0).random().

EDIT I think today (Swift 4) I'd write that something like this:

extension ClosedRange where Bound : FloatingPoint {
public func random() -> Bound {
let max = UInt32.max
return
Bound(arc4random_uniform(max)) /
Bound(max) *
(upperBound - lowerBound) +
lowerBound
}
}

NOTE Swift 4.2 introduces native random number generation and all of this becomes moot.

Based on YannickSteph's answer

To make it work for any floating point type, like Double, Float, CGFloat, etc., you can make an extension for the BinaryFloatingPoint type:

extension BinaryFloatingPoint {


/// Returns a random floating point number between 0.0 and 1.0, inclusive.
public static var random: Self {
return Self(arc4random()) / 0xFFFFFFFF
}


/// Random double between 0 and n-1.
///
/// - Parameter n:  Interval max
/// - Returns:      Returns a random double point number between 0 and n max
public static func random(min: Self, max: Self) -> Self {
return Self.random * (max - min) + min
}
}

Details

Xcode: 9.2, Swift 4

Solution

extension BinaryInteger {


static func rand(_ min: Self, _ max: Self) -> Self {
let _min = min
let difference = max+1 - _min
return Self(arc4random_uniform(UInt32(difference))) + _min
}
}


extension BinaryFloatingPoint {


private func toInt() -> Int {
// https://stackoverflow.com/q/49325962/4488252
if let value = self as? CGFloat {
return Int(value)
}
return Int(self)
}


static func rand(_ min: Self, _ max: Self, precision: Int) -> Self {


if precision == 0 {
let min = min.rounded(.down).toInt()
let max = max.rounded(.down).toInt()
return Self(Int.rand(min, max))
}


let delta = max - min
let maxFloatPart = Self(pow(10.0, Double(precision)))
let maxIntegerPart = (delta * maxFloatPart).rounded(.down).toInt()
let randomValue = Int.rand(0, maxIntegerPart)
let result = min + Self(randomValue)/maxFloatPart
return Self((result*maxFloatPart).toInt())/maxFloatPart
}
}

Usage

print("\(Int.rand(1, 20))")
print("\(Float.rand(5.231233, 44.5, precision: 3))")
print("\(Double.rand(5.231233, 44.5, precision: 4))")
print("\(CGFloat.rand(5.231233, 44.5, precision: 6))")

Full Sample

import Foundation
import CoreGraphics


func run() {
let min = 2.38945
let max = 2.39865
for _ in 0...100 {
let precision = Int.rand(0, 5)
print("Precision: \(precision)")
floatSample(min: Float(min), max: Float(max), precision: precision)
floatSample(min: Double(min), max: Double(max), precision: precision)
floatSample(min: CGFloat(min), max: CGFloat(max), precision: precision)
intSample(min: Int(1), max: Int(10000))
print("")
}
}


private func printResult<T: Comparable>(min: T, max: T, random: T) {
let result = "\(T.self) rand[\(min), \(max)] = \(random)"
print(result)
}


func floatSample<T: BinaryFloatingPoint>(min: T, max: T, precision: Int) {
printResult(min: min, max: max, random: T.rand(min, max, precision: precision))
}


func intSample<T: BinaryInteger>(min: T, max: T) {
printResult(min: min, max: max, random: T.rand(min, max))
}

Results

enter image description here

drand48()

In case you need [Double]. Between 0 and 1. That's all.

swift 4.2 :

let randomFloat = Float.random(in: 0..<1)

Swift 5

let randomFloat = CGFloat.random(in: 0...1)