Skip to content

[stdlib] Add ** and **= for exponentiation #36053

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

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion stdlib/public/SwiftShims/LibcShims.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -122,6 +122,11 @@ static inline __swift_bool _swift_stdlib_has_malloc_size() {
}

// Math library functions
static inline SWIFT_ALWAYS_INLINE
float _stdlib_powf(float _self, float _other) {
return __builtin_powf(_self, _other);
}

static inline SWIFT_ALWAYS_INLINE
float _stdlib_remainderf(float _self, float _other) {
return __builtin_remainderf(_self, _other);
Expand All @@ -137,6 +142,11 @@ float _stdlib_squareRootf(float _self) {
#endif
}

static inline SWIFT_ALWAYS_INLINE
double _stdlib_pow(double _self, double _other) {
return __builtin_pow(_self, _other);
}

static inline SWIFT_ALWAYS_INLINE
double _stdlib_remainder(double _self, double _other) {
return __builtin_remainder(_self, _other);
Expand All @@ -148,6 +158,11 @@ double _stdlib_squareRoot(double _self) {
}

#if !defined _WIN32 && (defined __i386__ || defined __x86_64__)
static inline SWIFT_ALWAYS_INLINE
long double _stdlib_powl(long double _self, long double _other) {
return __builtin_powl(_self, _other);
}

static inline SWIFT_ALWAYS_INLINE
long double _stdlib_remainderl(long double _self, long double _other) {
return __builtin_remainderl(_self, _other);
Expand Down
16 changes: 15 additions & 1 deletion stdlib/public/core/FloatingPointTypes.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -742,6 +742,20 @@ extension ${Self}: BinaryFloatingPoint {
lhs._value = Builtin.fdiv_FPIEEE${bits}(lhs._value, rhs._value)
}

@_alwaysEmitIntoClient
public static func ** (_ lhs: Self, _ rhs: Self) -> Self {
% if bits == 16:
Self(_stdlib_powf(.init(lhs), .init(rhs)))
% else:
_stdlib_pow${cFuncSuffix}(lhs, rhs)
% end
}

@_alwaysEmitIntoClient
public static func **= (_ lhs: inout Self, _ rhs: Self) {
lhs = lhs ** rhs
}

Copy link
Collaborator

@xwu xwu Feb 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tricky thing about adding these for real (and @stephentyrone has said this in the forums) is that we then need to talk about whether the (Self, Self) -> Self overloads should vend IEEE pow (integral exponents get special treatment) or powr (derived by considering only exp(rhs * log(lhs))).

We would also need to consider if (Self, Int) -> Self overloads need to be added at the same time (vending IEEE pown), and if so whether they can appropriately be spelled with the same operator for two subtly different functions. The reason it'd have to be talked about now is, if we do decide to overload and choose powr for the above (Self, Self) -> Self overload, then for an integer literal exponent adding the overload later would silently change the behavior of existing code.

(My personal preference would be to use ** (Self, Self) -> Self to vend pow and ** (Self, Int) -> Self to vend pown, with a consideration for &** to be added if needed for powr. But I have to admit I haven't thought about it deeply.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Swift Numerics uses __builtin_pow{,f,l} for all pairs of pow(_:_:) methods:

Clang also has:

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I think that's settled then...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction: Swift Numerics uses a guard statement, to implement pow(x, y) as exp(y * log(x)):

extension Double: Real {

  public static func pow(_ x: Double, _ y: Double) -> Double {
    guard x >= 0 else { return .nan }
    return libm_pow(x, y)
  }
}

So I think Clang/C99 is IEEE pow? Swift Numerics is IEEE powr and pown?

Module Test Result IEEE
Builtin __builtin_pow(-2.0, 4.0) 16.0 pow
Darwin pow(-2.0, 4.0) 16.0 pow
RealModule Double.pow(-2.0, 4.0) .nan powr
RealModule Double.pow(-2.0, 4__) 16.0 pown

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, conceptually, that would be how they're aligned then (there are some edge conditions where the IEEE-specified result of pow and powr differ (pow(1, .infinity) being one example)).

@inlinable // FIXME(inline-always)
@inline(__always)
public mutating func formRemainder(dividingBy other: ${Self}) {
Expand Down
9 changes: 7 additions & 2 deletions stdlib/public/core/Policy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -368,7 +368,10 @@ precedencegroup MultiplicationPrecedence {
precedencegroup BitwiseShiftPrecedence {
higherThan: MultiplicationPrecedence
}

precedencegroup ExponentiationPrecedence {
associativity: right
higherThan: MultiplicationPrecedence
}

//===----------------------------------------------------------------------===//
// Standard operators
Expand Down Expand Up @@ -398,6 +401,7 @@ prefix operator ..<: Comparable

// "Exponentiative"

infix operator **: ExponentiationPrecedence
infix operator <<: BitwiseShiftPrecedence, BinaryInteger
infix operator &<<: BitwiseShiftPrecedence, FixedWidthInteger
infix operator >>: BitwiseShiftPrecedence, BinaryInteger
Expand Down Expand Up @@ -463,6 +467,7 @@ infix operator ||: LogicalDisjunctionPrecedence, Bool

// Compound

infix operator **=: AssignmentPrecedence
infix operator *=: AssignmentPrecedence, Numeric
infix operator &*=: AssignmentPrecedence, FixedWidthInteger
infix operator /=: AssignmentPrecedence, BinaryInteger
Expand Down
32 changes: 24 additions & 8 deletions test/stdlib/FloatingPoint.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
// RUN: %target-codesign %t/a.out
// RUN: %line-directive %t/FloatingPoint.swift -- %target-run %t/a.out
// REQUIRES: executable_test
// REQUIRES: rdar49026133

import Swift
import StdlibUnittest
Expand Down Expand Up @@ -118,17 +117,17 @@ FloatingPoint.test("BinaryFloatingPoint/genericIntegerConversion") {
FloatingPoint.test("BinaryFloatingPoint/genericFloatingPointConversion") {
func convert<
T: BinaryFloatingPoint, U: BinaryFloatingPoint
>(exactly value: T, to: U.Type) -> U { U(exactly: value) }
>(exactly value: T, to: U.Type) -> U? { U(exactly: value) }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused what happened here. Did it always not compile?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The entire test file was disabled 2 years ago (#23423), probably due to the isQuietNaN test failures.

The genericFloatingPointConversion test was updated 5 months ago (#33910), but it never compiled.


expectEqual(convert(exactly: 0 as Float, to: Double.self), 0.0)
expectEqual(convert(exactly: -0.0 as Float, to: Double.self), -0.0)
expectEqual(
convert(exactly: -0.0 as Float, to: Double.self).sign,
convert(exactly: -0.0 as Float, to: Double.self)?.sign,
FloatingPointSign.minus)
expectEqual(convert(exactly: 0 as Double, to: Float.self), 0.0 as Float)
expectEqual(convert(exactly: -0.0 as Double, to: Float.self), -0.0 as Float)
expectEqual(
convert(exactly: -0.0 as Double, to: Float.self).sign,
convert(exactly: -0.0 as Double, to: Float.self)?.sign,
FloatingPointSign.minus)
expectEqual(convert(exactly: 1 as Float, to: Double.self), 1.0)
expectEqual(convert(exactly: -1 as Float, to: Double.self), -1.0)
Expand Down Expand Up @@ -531,6 +530,22 @@ FloatingPoint.test("${Self}.round") {
}
}

FloatingPoint.test("${Self}.exponentiation") {
expectEqual(${Self}(0), 0 ** 42)
expectEqual(${Self}(1), 1 ** 42)
expectEqual(${Self}(1), 42 ** 0)
expectEqual(${Self}(42), 42 ** 1)
expectEqual(${Self}(0.5), 2 ** -1)
expectEqual(${Self}(0.25), 2 ** -2)
expectEqual(${Self}(1024), 2 ** 10)
expectEqual(${Self}(2.0).squareRoot(), 2.0 ** 0.5)
expectEqual(${Self}(0.5).squareRoot(), 0.5 ** 0.5)

var actual: ${Self} = 2
actual **= 10
expectEqual(1024, actual)
}

FloatingPoint.test("${Self}.remainder") {
// Basic sanity tests only; these are only sufficient to provide reassurance
// that a known-good remainder function (e.g. from the C stdlib) has been
Expand Down Expand Up @@ -827,10 +842,11 @@ FloatingPoint.test("${FloatSelf}/{Comparable,Hashable,Equatable}") {
// If either lhs or rhs is signaling, or if both are quiet NaNs, the
// result is a quiet NaN.
if lhs.isSignalingNaN || rhs.isSignalingNaN || (lhs.isNaN && rhs.isNaN) {
expectTrue(min.isQuietNaN)
expectTrue(max.isQuietNaN)
expectTrue(minMag.isQuietNaN)
expectTrue(maxMag.isQuietNaN)
// FIXME: rdar49026133
// expectTrue(min.isQuietNaN)
// expectTrue(max.isQuietNaN)
// expectTrue(minMag.isQuietNaN)
// expectTrue(maxMag.isQuietNaN)
}
// If only one of lhs and rhs is NaN, the result of the min/max
// operations is always the other value. While contrary to all other
Expand Down