Skip to content

Add availability information to the new Math function protocols #24187

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

Merged
Merged
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
14 changes: 11 additions & 3 deletions stdlib/public/Darwin/CoreGraphics/CGFloat.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ public func %=(lhs: inout CGFloat, rhs: CGFloat) {

%from SwiftMathFunctions import *

extension CGFloat: Real {
extension CGFloat: ElementaryFunctions {
% for func in ElementaryFunctions + RealFunctions:

@_alwaysEmitIntoClient
Expand Down Expand Up @@ -553,12 +553,20 @@ extension CGFloat: Real {
return CGFloat(NativeType.atan2(y: y.native, x: x.native))
}

#if !os(Windows)
@_alwaysEmitIntoClient
public static func logGamma(_ x: CGFloat) -> CGFloat {
return CGFloat(NativeType.logGamma(x.native))
}
#endif

@_alwaysEmitIntoClient
public static func signGamma(_ x: CGFloat) -> FloatingPointSign {
if x >= 0 { return .plus }
let trunc = x.rounded(.towardZero)
if x == trunc { return .plus }
let halfTrunc = trunc/2
if halfTrunc == halfTrunc.rounded(.towardZero) { return .minus }
return .plus
}
}

//===----------------------------------------------------------------------===//
Expand Down
125 changes: 59 additions & 66 deletions stdlib/public/core/MathFunctions.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import SwiftShims
/// ElementaryFunctions and FloatingPoint.
///
/// [elfn]: http://en.wikipedia.org/wiki/Elementary_function
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
public protocol ElementaryFunctions {

%for func in ElementaryFunctions:
Expand All @@ -59,77 +60,12 @@ public protocol ElementaryFunctions {
static func root(_ x: Self, _ n: Int) -> Self
}

/// A type that models the real numbers.
///
/// Conformance to this protocol means that all the FloatingPoint operations
/// are available, as well as the ElementaryFunctions, plus the following
/// additional math functions: atan2, erf, erc, hypot, tgamma.
///
/// logGamma and signGamma are also available on non-Windows platforms.
public protocol Real: ElementaryFunctions, FloatingPoint {
%for func in RealFunctions:

${func.comment}
static func ${func.decl("Self")}
%end

/// `atan(y/x)` with quadrant fixup.
///
/// There is an infinite family of angles whose tangent is `y/x`. `atan2`
/// selects the representative that is the angle between the vector `(x, y)`
/// and the real axis in the range [-π, π].
static func atan2(y: Self, x: Self) -> Self

#if !os(Windows)
// lgamma is not available on Windows.
// TODO: provide an implementation of lgamma with the stdlib to support
// Windows so we can vend a uniform interface.

/// `log(gamma(x))` computed without undue overflow.
///
/// `log(abs(gamma(x)))` is returned. To get the sign of `gamma(x)` cheaply,
/// use `signGamma(x)`.
static func logGamma(_ x: Self) -> Self
#endif
}

extension Real {
#if !os(Windows)
// lgamma is not available on Windows; no lgamma means signGamma
// is basically useless, so don't bother exposing it.

/// The sign of `gamma(x)`.
///
/// This function is typically used in conjunction with `logGamma(x)`, which
/// computes `log(abs(gamma(x)))`, to recover the sign information that is
/// lost to the absolute value.
///
/// `gamma(x)` has a simple pole at each non-positive integer and an
/// essential singularity at infinity; we arbitrarily choose to return
/// `.plus` for the sign in those cases. For all other values, `signGamma(x)`
/// is `.plus` if `x >= 0` or `trunc(x)` is odd, and `.minus` otherwise.
@_alwaysEmitIntoClient
public static func signGamma(_ x: Self) -> FloatingPointSign {
if x >= 0 { return .plus }
let trunc = x.rounded(.towardZero)
// Treat poles as gamma(x) == +inf. This is arbitrary, but we need to
// pick one sign or the other.
if x == trunc { return .plus }
// Result is .minus if trunc is even, .plus otherwise. To figure out if
// trunc is even or odd, check if trunc/2 is an integer.
let halfTrunc = trunc/2
if halfTrunc == halfTrunc.rounded(.towardZero) { return .minus }
return .plus
}
#endif
}

%for type in all_floating_point_types():
% if type.bits == 80:
#if (arch(i386) || arch(x86_64)) && !os(Windows)
% end
% Self = type.stdlib_name
extension ${Self}: Real {
extension ${Self}: ElementaryFunctions {
% for func in ElementaryFunctions + RealFunctions:

@_alwaysEmitIntoClient
Expand Down Expand Up @@ -172,9 +108,66 @@ extension ${Self}: Real {
public static func logGamma(_ x: ${Self}) -> ${Self} {
return _swift_stdlib_lgamma${type.cFuncSuffix}(x)
}

@_alwaysEmitIntoClient
public static func signGamma(_ x: ${Self}) -> FloatingPointSign {
if x >= 0 { return .plus }
let trunc = x.rounded(.towardZero)
if x == trunc { return .plus }
let halfTrunc = trunc/2
if halfTrunc == halfTrunc.rounded(.towardZero) { return .minus }
return .plus
}
#endif
}
% if type.bits == 80:
#endif
% end
%end

@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
extension SIMD where Scalar: ElementaryFunctions {
% for func in ElementaryFunctions:

@_alwaysEmitIntoClient
public static func ${func.decl("Self")} {
var r = Self()
for i in r.indices {
r[i] = Scalar.${func.swiftName}(${func.params(suffix="[i]")})
}
return r
}
% end

@_alwaysEmitIntoClient
public static func pow(_ x: Self, _ y: Self) -> Self {
var r = Self()
for i in r.indices {
r[i] = Scalar.pow(x[i], y[i])
}
return r
}

@_alwaysEmitIntoClient
public static func pow(_ x: Self, _ n: Int) -> Self {
var r = Self()
for i in r.indices {
r[i] = Scalar.pow(x[i], n)
}
return r
}

@_alwaysEmitIntoClient
public static func root(_ x: Self, _ n: Int) -> Self {
var r = Self()
for i in r.indices {
r[i] = Scalar.root(x[i], n)
}
return r
}
}

%for n in [2,3,4,8,16,32,64]:
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
extension SIMD${n}: ElementaryFunctions where Scalar: ElementaryFunctions { }
%end
21 changes: 4 additions & 17 deletions test/stdlib/MathFunctions.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func expectEqualWithTolerance<T>(_ expected: TestLiteralType, _ actual: T,

%from SwiftMathFunctions import *

@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
internal extension ElementaryFunctions where Self: BinaryFloatingPoint {
static func elementaryFunctionTests() {
/* Default tolerance is 3 ulps unless specified otherwise. It's OK to relax
Expand Down Expand Up @@ -78,21 +79,6 @@ internal extension ElementaryFunctions where Self: BinaryFloatingPoint {
}
}

internal extension Real where Self: BinaryFloatingPoint {
static func realFunctionTests() {
expectEqualWithTolerance(0.54041950027058415544357836460859991, Self.atan2(y: 0.375, x: 0.625))
expectEqualWithTolerance(0.72886898685566255885926910969319788, Self.hypot(0.375, 0.625))
expectEqualWithTolerance(0.4041169094348222983238250859191217675, Self.erf(0.375))
expectEqualWithTolerance(0.5958830905651777016761749140808782324, Self.erfc(0.375))
expectEqualWithTolerance(2.3704361844166009086464735041766525098, Self.gamma(0.375))
#if !os(Windows)
expectEqualWithTolerance( -0.11775527074107877445136203331798850, Self.logGamma(1.375), ulps: 16)
expectEqual(.plus, Self.signGamma(1.375))
expectEqual(.minus, Self.signGamma(-2.375))
#endif
}
}

%for T in ['Float', 'Double', 'CGFloat', 'Float80']:
% if T == 'Float80':
#if (arch(i386) || arch(x86_64)) && !os(Windows)
Expand All @@ -102,8 +88,9 @@ internal extension Real where Self: BinaryFloatingPoint {
% end

MathTests.test("${T}") {
${T}.elementaryFunctionTests()
${T}.realFunctionTests()
if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) {
${T}.elementaryFunctionTests()
}
}

% if T in ['CGFloat', 'Float80']:
Expand Down
2 changes: 1 addition & 1 deletion utils/SwiftMathFunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def decl(self, type):
return self.swiftName + "(" + self.params("_ ", ": " + type) + \
") -> " + type

def free_decl(self, constraint="T: ElementaryFunctions"):
def freeDecl(self, constraint):
return self.swiftName + "<T>(" + self.params("_ ", ": T") + \
") -> T where " + constraint

Expand Down