Swift 中的指数运算符

Posted

技术标签:

【中文标题】Swift 中的指数运算符【英文标题】:Exponentiation operator in Swift 【发布时间】:2014-07-26 18:39:53 【问题描述】:

我没有在 Swift 语言参考中看到基本算术运算符中定义的幂运算符。

语言中真的没有预定义的整数或浮点取幂运算符吗?

【问题讨论】:

就我而言,这是解决方案:***.com/a/28710112/2161007 【参考方案1】:

没有运算符,但您可以像这样使用 pow 函数:

return pow(num, power)

如果你愿意,你也可以让操作员像这样调用 pow 函数:

infix operator **  associativity left precedence 170 

func ** (num: Double, power: Double) -> Double
    return pow(num, power)


2.0**2.0 //4.0

【讨论】:

最好使用**,这样你就可以在整数上使用它并且不会与XOR冲突。 ^ 运算符在 Swift 中被定义为 XOR。 警告!这里有一个问题。例如,通常在编程语言中,-2.0**2.0 = -(2.0**2.0) = -4.0。但是,这里是-2.0**2.0 = (-2.0)**2.0 = 4.0,这可能不是预期用途,可能会导致非常讨厌且难以追踪的错误。 NSHipster 使用类似的描述,但优先级为 160 以匹配 <<>>。不同的优先级会导致不同的代码解释,因此对通用运算符的优先级进行标准化很重要。我不知道最好的标准是什么,但是给予<< 2** 2 相同的优先级是有道理的。 nshipster.com/swift-operators 取幂不是右结合吗? en.wikipedia.org/wiki/Operator_associativity【参考方案2】:

如果您碰巧将 2 提高到某个幂,则可以使用按位左移运算符:

let x = 2 << 0    // 2
let y = 2 << 1    // 4
let z = 2 << 7    // 256

请注意,“功率”值比您想象的要小 1。

请注意,这比 pow(2.0, 8.0) 更快,并且可以避免使用双打。

【讨论】:

这很好,但并没有真正回答问题。 我对 2 的幂感兴趣,所以它回答了我。 @chanceoperation 或者,对于 2 的 n 次方,您可以将 1 或 0b00000001 向左移动 n 个位置 Swift Advanced Operators let x = 0b00000001 &lt;&lt; exponent // 2**exponent let x = 1 &lt;&lt; 0 // 1 let x = 1 &lt;&lt; 2 // 4 let x = 1 &lt;&lt; 8 // 256 你应该做1 &lt;&lt; exponent,因为你实际上是在一个小的字节序值上向左移动位。 1 &lt;&lt; 1 == 2,或者想象 00000001 得到左移 1,得到 00000010,即二进制 2。我喜欢这个解决方案。我将其合并到下面的comprehensive answer 中。【参考方案3】:

对于正在寻找** 中缀运算符的 Swift 3 版本的任何人:

precedencegroup ExponentiationPrecedence 
  associativity: right
  higherThan: MultiplicationPrecedence


infix operator ** : ExponentiationPrecedence

func ** (_ base: Double, _ exp: Double) -> Double 
  return pow(base, exp)


func ** (_ base: Float, _ exp: Float) -> Float 
  return pow(base, exp)


2.0 ** 3.0 ** 2.0    // 512
(2.0 ** 3.0) ** 2.0  // 64

【讨论】:

不错。别忘了import Darwin 获取pow 很确定关联性应该在左边,而不是右边。 2^3^2 是 64,而不是 512。 嗯,在 Python 和 javascript 中,2**3**2 是 512,而不是 64。我不知道任何具有左关联幂运算符的编程语言。它们都是右联想。如果你在 Swift 中实现它,你绝对应该使用右关联来与其他流行语言保持一致,以及mathematical convention。【参考方案4】:

我是这样做的:

operator infix **  associativity left precedence 200 

func ** (base: Double, power: Double) -> Double 
    return exp(log(base) * power)

【讨论】:

这似乎......效率低下【参考方案5】:

斯威夫特 4.2

import Foundation

var n = 2.0 // decimal
var result = 5 * pow(n, 2)
print(result)
// 20.0

【讨论】:

【参考方案6】:

没有,但你有 pow 函数。

【讨论】:

【参考方案7】:

像大多数 C 系列语言一样,没有一种。

【讨论】:

【参考方案8】:

如果您对 Int 类型的求幂运算符特别感兴趣,我认为现有的答案对于大数不会特别有效,因为浮点数在内存中的表示方式。当从Int 转换为FloatDouble 然后返回(这是Darwin 模块中的powpowfpowl 函数所必需的)you may lose precision。这是Int 的精确版本:

let pow =  Array(repeating: $0, count: $1).reduce(1, *) 

请注意,此版本的内存效率不是特别高,并且针对源代码大小进行了优化。

另一个不会创建中间数组的版本:

func pow(_ x: Int, _ y: Int) -> Int 
  var result = 1
  for i in 0..<y 
    result *= x
  
  return result

【讨论】:

此函数未能通过测试用例。负指数的正确行为是什么?似乎这个 pow 函数会将它们全部舍入到 1,这可能是可以接受的,直到基数也是负数,例如-1**-1 = -1, -10**-1 = -0.1. 负幂的正确行为可能与将 pow(Double, Double) 结果转换为 Int 得到的结果相匹配。我已经为该解决方案提供了new answer,但还通过处理边界情况和使用位左移运算符来优化速度以实现 2 和 -2 的幂。【参考方案9】:

另一种答案是使用 NSExpression

let mathExpression = NSExpression(format:"2.5**2.5")
let answer = mathExpression.expressionValue(with: nil, context: nil) as? Double

let mathExpression = NSExpression(format:"2**3")
let answer = mathExpression.expressionValue(with: nil, context: nil) as? Int

【讨论】:

【参考方案10】:

这个答案提供了一个经过测试和优化的*函数,用于计算整数的整数幂,同时还提供了几个版本的自定义 ** 运算符用于求幂。

* 根据我在此页面上阅读的内容,至少我认为它已经过优化。

我的猜测是 Swift 故意不提供此功能,因为需要选择如何处理绝对值小于 1 的结果。您希望它舍入为 0 还是隐式转换为十进制类型?编译器不知道,选择默认值可能会导致人们在使用它时没有意识到他们刚刚做了什么数学选择。

在 Swift 5.3 中:

import Foundation

precedencegroup ExponeniationPrecedence 
    associativity: right  // This makes Towers of Powers work correctly
    higherThan: MultiplicationPrecedence


infix operator ** : ExponeniationPrecedence

public func **(_ base: Int, _ exponent: Int) -> Int 
    return pow(base, exponent)

public func **(_ base: Double, _ exponent: Double) -> Double 
    return pow(base, exponent)

public func **(_ base: Decimal, _ exponent: Int) -> Decimal 
    return pow(base, exponent)

public func **(_ base: Float, _ exponent: Float) -> Float 
    return powf(base, exponent)


/// Calculate exponentiation of integer base and integer exponent, returning integer result.
/// 
/// Exponentiation that would result in absolute values of less than 1 (i.e. exponent is negative and base is not 1 or -1) are rounded 0.
public func pow(_ base: Int, _ exponent: Int) -> Int 
    // Optimize cases for certain exponents
    switch exponent 
    case 0:
        return 1 
    case 1:
        return base
    case _ where exponent < 0 && base != -1 && base != 1:
        // Negative exponents of integers always round to zero, except if the base is 1 or -1
        return 0
    default:
        break 
    
    
    // Optimize cases for certain bases
    switch base 
    case -1:
        if exponent % 2 == 0 
            return -1 * base
         else 
            return base
        
    case 0, 1:
        return base 
    case -2, 2:
        // Use the bitwise left shift operator to efficiently calculate powers of 2 and -2
        let result = 1 << exponent
        if base == -2 && exponent % 2 == 1 
            return -1 * result
        
        return result
    default:
        var result = 1
        for i in 1 ... exponent 
            result *= base
        
        return result
    


/// Calculate powers of integer base and integer exponent using Foundation's pow function by casting both the base and the exponent as Doubles, calling pow, but without casting the result.
/// Useful if rounding results between -1 and 1 to zero is not acceptable. 
public func pow(_ base: Int, _ exponent: Int) -> Double 
    return pow(Double(base), Double(exponent))


/// Calculate powers of integer base and integer exponent using Foundation's pow function by casting both the base and the exponent as Doubles, calling pow, and then casting the result as an Int
/// If results are -1<x<1, round to 0.
public func castPow(_ base: Int, _ exponent: Int) -> Int 
    return Int(pow(base, exponent))


pow(Int, Int) 函数的测试用例:


// Test Exponent = 0
assert(0**0 == 1)
assert(1**0 == 1)
assert(2**0 == 1)

// Test Exponent = 1
assert(-1**1 == -1)
assert(0**1 == 0)
assert(1**1 == 1)
assert(2**1 == 2)

// Test Exponent = -1
assert(-1 ** -1 == -1)
assert(0 ** -1 == 0)
assert(1 ** -1 == 1)
assert(2 ** -1 == 0)

// Test Exponent = 2
assert(-1 ** 2 == 1)
assert(0 ** 2 == 0)
assert(1 ** 2 == 1)
assert(2 ** 2 == 4)
assert(3 ** 2 == 9)

// Test Base = 0
assert(0**0 == 1)
assert(0**1 == 0)
assert(0**2 == 0)

// Test Base = 1
assert(1 ** -1 == 1)
assert(1**0 == 1)
assert(1**1 == 1)
assert(1**2 == 1)

// Test Base = -1
assert(-1 ** -1 == -1)
assert(-1**0 == 1)
assert(-1**1 == -1)
assert(-1**2 == 1)
assert(-1**2 == 1)
assert(-1**3 == -1)

// Test Base = 2
assert(2 ** -1 == 0)
assert(2**0 == 1)
assert(2**1 == 2)
assert(2**2 == 4)
assert(2**3 == 8)

// Test Base = -2
assert(-2 ** -1 == 0)
assert(-2**0 == 1)
assert(-2**1 == -2)
assert(-2**2 == 4)
assert(-2**3 == -8)

// Test Base = 3
assert(3 ** -1 == 0)
assert(3**0 == 1)
assert(3**1 == 3)
assert(3**2 == 9)
assert(3**3 == 27)

// Test Towers of Powers
assert(2**2**2 == 16)
assert(3**2**2 == 81)
assert(2**2**3 == 256)
assert(2**3**2 == 512)

【讨论】:

【参考方案11】:

如何在 Swift 5.5 中复制 ** 运算符

标准库旨在保持精简和小型,因此它们不包含所有算术函数。它们实际上是 BinaryIntegerBinaryFloatingPoint 可能组合的 4 种情况。 我已经看到了使用DoubleInt 的答案,这会产生各种令人困惑的编译器错误。使用generics 的美,我们可以覆盖所有情况,编译器不会再抱怨了:

infix operator **

func **<I: BinaryInteger>(lhs: I, rhs: I) -> I 
    return I(pow(Double(lhs), Double(rhs)))


func **<I: BinaryInteger, F: BinaryFloatingPoint>(lhs: I, rhs: F) -> F 
    return F(pow(Double(lhs), Double(rhs)))


func **<I: BinaryInteger, F: BinaryFloatingPoint>(lhs: F, rhs: I) -> F 
    return F(pow(Double(lhs), Double(rhs)))


func **<F: BinaryFloatingPoint>(lhs: F, rhs: F) -> F 
    return F(pow(Double(lhs), Double(rhs)))


【讨论】:

以上是关于Swift 中的指数运算符的主要内容,如果未能解决你的问题,请参考以下文章

c语言中的指数运算

什么是Kotlin指数运算符

指数运算符 ^ 和 Math.pow() 之间的区别

C中的几个复杂的指数运算返回inf? [复制]

swift swift中的关联附加运算符

swift swift中的自定义随机数运算符