mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
330 lines
9.9 KiB
Swift
330 lines
9.9 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
|
|
/// ```
|
|
///
|
|
/// All of these are made available as free functions by importing the Math
|
|
/// module:
|
|
///
|
|
/// ```swift
|
|
/// import Math
|
|
/// let y = 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.
|
|
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
|
|
|
|
// MARK: - Free functions defined on ElementaryFunctions:
|
|
%from SwiftMathFunctions import *
|
|
%for func in ElementaryFunctions:
|
|
|
|
% if func.name == 'sqrt':
|
|
// sqrt requires availability because it was previous generic on FloatingPoint.
|
|
@available(swift, introduced: 5.1)
|
|
% end
|
|
@_alwaysEmitIntoClient
|
|
public func ${func.free_decl()} {
|
|
return T.${func.swiftName}(${func.params()})
|
|
}
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func ${func.free_decl("T: SIMD, T.Scalar: ElementaryFunctions")} {
|
|
var r = T()
|
|
for i in r.indices { r[i] = T.Scalar.${func.swiftName}(${func.params("","[i]")}) }
|
|
return r
|
|
}
|
|
%end
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func pow<T>(_ x: T, _ y: T) -> T where T: ElementaryFunctions {
|
|
return T.pow(x, y)
|
|
}
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func pow<T>(_ x: T, _ y: T) -> T where T: SIMD, T.Scalar: ElementaryFunctions {
|
|
var r = T()
|
|
for i in r.indices { r[i] = T.Scalar.pow(x[i], y[i]) }
|
|
return r
|
|
}
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func pow<T>(_ x: T, _ n: Int) -> T where T: ElementaryFunctions {
|
|
return T.pow(x, n)
|
|
}
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func pow<T>(_ x: T, _ n: Int) -> T where T: SIMD, T.Scalar: ElementaryFunctions {
|
|
var r = T()
|
|
for i in r.indices { r[i] = T.Scalar.pow(x[i], n) }
|
|
return r
|
|
}
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func root<T>(_ x: T, _ n: Int) -> T where T: ElementaryFunctions {
|
|
return T.root(x, n)
|
|
}
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func root<T>(_ x: T, _ n: Int) -> T where T: SIMD, T.Scalar: ElementaryFunctions {
|
|
var r = T()
|
|
for i in r.indices { r[i] = T.Scalar.root(x[i], n) }
|
|
return r
|
|
}
|
|
|
|
// MARK: - Free functions defined on Real:
|
|
%for func in RealFunctions:
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func ${func.free_decl("T: Real")} {
|
|
return T.${func.swiftName}(${func.params()})
|
|
}
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func ${func.free_decl("T: SIMD, T.Scalar: Real")} {
|
|
var r = T()
|
|
for i in r.indices { r[i] = T.Scalar.${func.swiftName}(${func.params("","[i]")}) }
|
|
return r
|
|
}
|
|
%end
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func atan2<T>(y: T, x: T) -> T where T: Real {
|
|
return T.atan2(y: y, x: x)
|
|
}
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func atan2<T>(y: T, x: T) -> T where T: SIMD, T.Scalar: Real {
|
|
var r = T()
|
|
for i in r.indices { r[i] = T.Scalar.atan2(y: y[i], x: x[i]) }
|
|
return r
|
|
}
|
|
|
|
// logGamma is not available on Windows.
|
|
// TODO: logGamma, signGamma for SIMD types.
|
|
#if !os(Windows)
|
|
@_alwaysEmitIntoClient
|
|
public func logGamma<T>(_ x: T) -> T where T: Real {
|
|
return T.logGamma(x)
|
|
}
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func signGamma<T>(_ x: T) -> FloatingPointSign where T: Real {
|
|
return T.signGamma(x)
|
|
}
|
|
#endif
|
|
|
|
// MARK: - Free functions defined on FloatingPoint:
|
|
% for (func,dir) in [("ceil", ".up"), ("floor", ".down"), ("round", ""), ("trunc", ".towardZero")]:
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func ${func}<T>(_ x: T) -> T where T: FloatingPoint {
|
|
return x.rounded(${dir})
|
|
}
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func ${func}<T>(_ x: T) -> T where T: SIMD, T.Scalar: FloatingPoint {
|
|
return x.rounded(${dir})
|
|
}
|
|
%end
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func fma<T>(_ x: T, _ y: T, _ z: T) -> T where T: FloatingPoint {
|
|
return z.addingProduct(x, y)
|
|
}
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func fma<T>(_ x: T, _ y: T, _ z: T) -> T where T: SIMD, T.Scalar: FloatingPoint {
|
|
return z.addingProduct(x, y)
|
|
}
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func remainder<T>(_ dividend: T, _ divisor: T) -> T where T: FloatingPoint {
|
|
return dividend.remainder(dividingBy: divisor)
|
|
}
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func remainder<T>(_ dividend: T, _ divisor: T) -> T where T: SIMD, T.Scalar: FloatingPoint {
|
|
var r = T()
|
|
for i in r.indices { r[i] = dividend[i].remainder(dividingBy: divisor[i]) }
|
|
return r
|
|
}
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func copysign<T>(_ magnitude: T, _ sign: T) -> T where T: FloatingPoint {
|
|
return T(signOf: sign, magnitudeOf: magnitude)
|
|
}
|
|
|
|
@_alwaysEmitIntoClient
|
|
public func copysign<T>(_ magnitude: T, _ sign: T) -> T where T: SIMD, T.Scalar: FloatingPoint {
|
|
var r = T()
|
|
for i in r.indices { r[i] = copysign(magnitude[i], sign[i]) }
|
|
return r
|
|
}
|