【问题标题】:Swift random float between 0 and 10 到 1 之间的 Swift 随机浮点数
【发布时间】:2014-09-22 21:23:04
【问题描述】:

在 Swift 中,我试图在 0 和 1 之间获得一个随机浮点数,但我似乎无法让类型转换起作用。

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

我收到“CGFloat”不可转换为“UInt8”错误

运行 Xcode 6。

【问题讨论】:

标签: swift random arc4random cgfloat


【解决方案1】:

这是Int、Double、Float、CGFloat

随机数的扩展

Swift 3 & 4 & 5 语法

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
    }
}

使用:

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)

【讨论】:

  • @DaRk-_-D0G 我试图理解数学。为什么 Float 和 Double 的 random() 除以 0xFFFFFFFF?
  • @EthanMick 添加 Swift 2 外观更新 ;)
  • " cannot call value of non-function type 'CGFloat'" 尝试使用 CGFloat.random(min:0.1, max:10.1) 时出现错误
  • Swift 4 不需要更新。
  • @YannickSteph 使用 Swift 5 和 Xcode 10.2,return Float(arc4random()) / 0xFFFFFFFF 行现在会发出警告:'4294967295' is not exactly representable as 'Float'; it becomes '4294967296'。知道如何解决该警告吗?我正在考虑除以Float(UInt32.max)
【解决方案2】:

斯威夫特 5

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

【讨论】:

    【解决方案3】:

    为 Swift 3 更新 Sandy Chapman's answer

    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
        }
    }
    

    现在你可以说像(-1.0...1.0).random() 这样的东西了。

    编辑我认为今天(Swift 4)我会这样写:

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

    注意 Swift 4.2 引入了本机随机数生成,所有这些都变得毫无意义。

    【讨论】:

      【解决方案4】:

      swift 4.2:

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

      【讨论】:

        【解决方案5】:
        drand48()
        

        如果你需要[双]。介于 0 和 1 之间。仅此而已。

        【讨论】:

          【解决方案6】:

          详情

          Xcode:9.2,斯威夫特 4

          解决方案

          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
              }
          }
          

          用法

          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))")
          

          完整样本

          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))
          }
          

          结果

          【讨论】:

            【解决方案7】:

            基于 YannickSteph 的回答

            要使其适用于任何浮点类型,例如DoubleFloatCGFloat 等,您可以为BinaryFloatingPoint 类型进行扩展:

            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
                }
            }
            

            【讨论】:

              【解决方案8】:

              这里的框架在 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
                  }
              }
              

              【讨论】:

              • 这在为Generic iOS device 编译时不起作用。 0xffffffff 不适合 Int。试试这个 CGFloat(UInt32.max)
              【解决方案9】:

              下面是 IntervalType 类型的扩展:

              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
                  }
              }
              

              使用此扩展,您可以使用区间语法生成区间,然后在该区间中获取随机值:

              (0.0...1.0).random()
              

              加法

              如果您希望为Ints 做同样的事情,那么您可以在CollectionType 协议上使用以下扩展:

              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 不要使用IntervalType。他们改用Range。对 CollectionType 类型执行此操作的好处是它会自动转移到 DictionaryArray 类型。

              例子:

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

              【讨论】:

              • 我们如何让它也适用于 Ints,而不是最初的问题,但如果我们扩展 IntervalType,这似乎是下一步
              • 我真的很喜欢这个解决方案,但我正在更新以包含更好的通用实践
              • 在 Swift 3 中损坏。
              【解决方案10】:

              jmduke 的建议似乎可以在 Playground 中使用,只是对功能稍作改动:

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

              以及从swift doc和drawag中指出的原因:类型转换必须是显式的,文档中的示例是:

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

              【讨论】:

                【解决方案11】:

                尝试将除数也初始化为浮点数,a la:

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

                【讨论】:

                • 谢谢,我曾尝试使用 CGFloat 转换分母,但没有成功。但看起来好像先转换为浮点数,然后再转换为 CGFloat 工作。
                • 无需通过Float——只需CGFloat(arc4random()) / CGFloat(UInt32.max)
                • @Joe_Schmoe 如果你不通过 Float / CGFloat,除法是一个小整数除以一个大整数,并且总是返回 0。
                • 但在 swift 4.2 中只需使用:Float.random(in: 0..
                猜你喜欢
                • 2011-07-07
                • 2012-12-17
                • 2023-04-05
                • 2012-12-18
                • 1970-01-01
                • 2014-04-11
                • 1970-01-01
                相关资源
                最近更新 更多