mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
This implements the protocols and static functions proposed in SE-0246, plus some initial test coverage. It also has some rough accompanying cleanup of tgmath. It does not include the globals (on scalars or SIMD types) nor does it deprecate much in tgmath.h.
181 lines
6.2 KiB
Swift
181 lines
6.2 KiB
Swift
//===--- MathFunctions.swift ----------------------------------*- swift -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2019 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
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import SwiftShims
|
|
%from SwiftMathFunctions import *
|
|
%from SwiftFloatingPointTypes import all_floating_point_types
|
|
|
|
/// A type that has elementary functions available.
|
|
///
|
|
/// An ["elementary function"][elfn] is a function built up from powers, roots,
|
|
/// exponentials, logarithms, trigonometric functions (sin, cos, tan) and
|
|
/// their inverses, and the hyperbolic functions (sinh, cosh, tanh) and their
|
|
/// inverses.
|
|
///
|
|
/// Conformance to this protocol means that all of these building blocks are
|
|
/// available as static functions on the type.
|
|
///
|
|
/// ```swift
|
|
/// let x: Float = 1
|
|
/// let y = Float.sin(x) // 0.84147096
|
|
/// ```
|
|
///
|
|
/// Additional operations, such as `atan2(y:x:)`, `hypot(_:_:)` and some
|
|
/// special functions, are provided on the Real protocol, which refines both
|
|
/// ElementaryFunctions and FloatingPoint.
|
|
///
|
|
/// [elfn]: http://en.wikipedia.org/wiki/Elementary_function
|
|
public protocol ElementaryFunctions {
|
|
|
|
%for func in ElementaryFunctions:
|
|
|
|
${func.comment}
|
|
static func ${func.decl("Self")}
|
|
%end
|
|
|
|
/// `exp(y log(x))` computed without loss of intermediate precision.
|
|
///
|
|
/// For real types, if `x` is negative the result is NaN, even if `y` has
|
|
/// an integral value. For complex types, there is a branch cut on the
|
|
/// negative real axis.
|
|
static func pow(_ x: Self, _ y: Self) -> Self
|
|
|
|
/// `x` raised to the `n`th power.
|
|
static func pow(_ x: Self, _ n: Int) -> Self
|
|
|
|
/// The `n`th root of `x`.
|
|
///
|
|
/// For real types, if `x` is negative and `n` is even, the result is NaN.
|
|
/// For complex types, there is a branch cut along the negative real axis.
|
|
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 {
|
|
% for func in ElementaryFunctions + RealFunctions:
|
|
|
|
@_alwaysEmitIntoClient
|
|
public static func ${func.decl(Self)} {
|
|
return ${func.impl(type)}
|
|
}
|
|
% end
|
|
|
|
@_alwaysEmitIntoClient
|
|
public static func pow(_ x: ${Self}, _ y: ${Self}) -> ${Self} {
|
|
guard x >= 0 else { return .nan }
|
|
return ${Self}(Builtin.int_pow_FPIEEE${type.bits}(x._value, y._value))
|
|
}
|
|
|
|
@_alwaysEmitIntoClient
|
|
public static func pow(_ x: ${Self}, _ n: Int) -> ${Self} {
|
|
// TODO: this implementation isn't quite right for n so large that
|
|
// the conversion to `${Self}` rounds. We could also consider using
|
|
// a multiply-chain implementation for small `n`; this would be faster
|
|
// for static `n`, but less accurate on platforms with a good `pow`
|
|
// implementation.
|
|
return ${Self}(Builtin.int_pow_FPIEEE${type.bits}(x._value, ${Self}(n)._value))
|
|
}
|
|
|
|
@_alwaysEmitIntoClient
|
|
public static func root(_ x: ${Self}, _ n: Int) -> ${Self} {
|
|
guard x >= 0 || n % 2 != 0 else { return .nan }
|
|
// TODO: this implementation isn't quite right for n so large that
|
|
// the conversion to `${Self}` rounds.
|
|
return ${Self}(signOf: x, magnitudeOf: pow(x, 1/${Self}(n)))
|
|
}
|
|
|
|
@_alwaysEmitIntoClient
|
|
public static func atan2(y: ${Self}, x: ${Self}) -> ${Self} {
|
|
return _swift_stdlib_atan2${type.cFuncSuffix}(y, x)
|
|
}
|
|
|
|
#if !os(Windows)
|
|
@_alwaysEmitIntoClient
|
|
public static func logGamma(_ x: ${Self}) -> ${Self} {
|
|
return _swift_stdlib_lgamma${type.cFuncSuffix}(x)
|
|
}
|
|
#endif
|
|
}
|
|
% if type.bits == 80:
|
|
#endif
|
|
% end
|
|
%end
|