├── Sources ├── _NumericsShims │ ├── include │ │ └── module.modulemap │ ├── README.md │ ├── CMakeLists.txt │ └── _NumericsShims.c ├── Numerics │ ├── README.md │ ├── Numerics.swift │ └── CMakeLists.txt ├── CMakeLists.txt ├── RealModule │ ├── Documentation.docc │ │ ├── RealFunctions.md │ │ ├── RealModule.md │ │ ├── Augmented.md │ │ ├── ElementaryFunctions.md │ │ └── Relaxed.md │ ├── CMakeLists.txt │ ├── RelaxedArithmetic.swift │ ├── RealFunctions.swift │ ├── README.md │ ├── Float80+Real.swift │ ├── Float16+Real.swift │ ├── AlgebraicField.swift │ ├── Float+Real.swift │ ├── AugmentedArithmetic.swift │ ├── Real.swift │ └── Double+Real.swift ├── _TestSupport │ ├── BlackHole.swift │ ├── CMakeLists.txt │ ├── RealTestSupport.swift │ ├── Error.swift │ └── Interval.swift ├── IntegerUtilities │ ├── CMakeLists.txt │ ├── GCD.swift │ ├── Rotate.swift │ ├── README.md │ ├── SaturatingArithmetic.swift │ └── ShiftWithRounding.swift └── ComplexModule │ ├── Complex+IntegerLiteral.swift │ ├── Complex+StringConvertible.swift │ ├── CMakeLists.txt │ ├── Complex+AdditiveArithmetic.swift │ ├── Complex+Codable.swift │ ├── Scale.swift │ ├── Documentation.docc │ ├── Complex.md │ ├── Infinity.md │ ├── ComplexModule.md │ └── Magnitude.md │ ├── Complex+Hashable.swift │ ├── Complex+Numeric.swift │ ├── Polar.swift │ ├── README.md │ ├── Complex.swift │ └── Complex+AlgebraicField.swift ├── .gitignore ├── .spi.yml ├── CONTRIBUTING.md ├── Tests ├── RealTests │ ├── CMakeLists.txt │ ├── RelaxedArithmeticTests.swift │ ├── ApproximateEqualityTests.swift │ ├── ElementaryFunctionChecks.swift │ └── AugmentedArithmeticTests.swift ├── IntegerUtilitiesTests │ ├── CMakeLists.txt │ ├── GCDTests.swift │ ├── RotateTests.swift │ ├── ShiftTests.swift │ └── SaturatingArithmeticTests.swift └── ComplexTests │ ├── CMakeLists.txt │ ├── ApproximateEqualityTests.swift │ └── PropertyTests.swift ├── .github └── workflows │ └── pull_request.yml ├── CMakeLists.txt ├── Package.swift ├── cmake └── modules │ └── SwiftSupport.cmake └── README.md /Sources/_NumericsShims/include/module.modulemap: -------------------------------------------------------------------------------- 1 | module _NumericsShims { 2 | header "_NumericsShims.h" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /*XCTestManifests.swift 3 | /.build 4 | /Packages 5 | /*.xcodeproj 6 | /.swiftpm 7 | .*.sw? 8 | -------------------------------------------------------------------------------- /.spi.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | builder: 3 | configs: 4 | - documentation_targets: [Numerics, RealModule, ComplexModule] 5 | -------------------------------------------------------------------------------- /Sources/Numerics/README.md: -------------------------------------------------------------------------------- 1 | # Numerics 2 | 3 | This umbrella module provides an easy way to get access to *all* of the Swift Numerics 4 | API with a single import statement: 5 | ``` 6 | import Numerics 7 | ``` 8 | -------------------------------------------------------------------------------- /Sources/_NumericsShims/README.md: -------------------------------------------------------------------------------- 1 | # Numerics Shims 2 | 3 | This module provides no stable Swift API. 4 | It is an internal implementation detail of other Swift Numerics modules, providing access to builtins and assembly by wrapping them in static inline C functions. 5 | 6 | Within the standard library, this is achieved via the Builtin module instead, but because Swift Numerics is a separate project, it should not be built with the flags that are needed to enable access to the Builtin module. 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | By submitting a pull request, you represent that you have the right to license your 2 | contribution to Apple and the community, and agree by submitting the patch that 3 | your contributions are licensed under the [Swift license](https://swift.org/LICENSE.txt). 4 | 5 | --- 6 | 7 | Before submitting the pull request, please make sure you have tested your changes 8 | and that they follow the Swift project [guidelines for contributing 9 | code](https://swift.org/contributing/#contributing-code). 10 | -------------------------------------------------------------------------------- /Sources/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #[[ 2 | This source file is part of the Swift Numerics open source project 3 | 4 | Copyright (c) 2019-2021 Apple Inc. and the Swift Numerics project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See https://swift.org/LICENSE.txt for license information 8 | #]] 9 | 10 | add_subdirectory(_NumericsShims) 11 | add_subdirectory(ComplexModule) 12 | add_subdirectory(IntegerUtilities) 13 | add_subdirectory(Numerics) 14 | add_subdirectory(RealModule) 15 | if(BUILD_TESTING) 16 | add_subdirectory(_TestSupport) 17 | endif() 18 | -------------------------------------------------------------------------------- /Sources/RealModule/Documentation.docc/RealFunctions.md: -------------------------------------------------------------------------------- 1 | # ``RealFunctions`` 2 | 3 | A type that extends ``ElementaryFunctions`` with additional operations that 4 | are primarily used with real numbers. 5 | 6 | ## Topics 7 | 8 | ### Exponential functions 9 | - ``exp2(_:)`` 10 | - ``exp10(_:)`` 11 | 12 | ### Logarithmetic functions 13 | - ``log2(_:)`` 14 | - ``log10(_:)`` 15 | 16 | ### Plane geometry 17 | - ``atan2(y:x:)`` 18 | - ``hypot(_:_:)`` 19 | 20 | ### Gamma function 21 | - ``gamma(_:)`` 22 | - ``logGamma(_:)`` 23 | - ``signGamma(_:)`` 24 | 25 | ### Error function 26 | - ``erf(_:)`` 27 | - ``erfc(_:)`` 28 | -------------------------------------------------------------------------------- /Sources/_NumericsShims/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #[[ 2 | This source file is part of the Swift Numerics open source project 3 | 4 | Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See https://swift.org/LICENSE.txt for license information 8 | #]] 9 | 10 | add_library(_NumericsShims INTERFACE) 11 | target_include_directories(_NumericsShims INTERFACE 12 | include) 13 | target_link_libraries(_NumericsShims INTERFACE 14 | $<$:m>) 15 | 16 | set_property(GLOBAL APPEND PROPERTY SWIFT_NUMERICS_EXPORTS _NumericsShims) 17 | -------------------------------------------------------------------------------- /Sources/Numerics/Numerics.swift: -------------------------------------------------------------------------------- 1 | //===--- Numerics.swift ---------------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | // A module that re-exports the complete Swift Numerics public API. 13 | @_exported import ComplexModule 14 | @_exported import IntegerUtilities 15 | @_exported import RealModule 16 | -------------------------------------------------------------------------------- /Sources/_TestSupport/BlackHole.swift: -------------------------------------------------------------------------------- 1 | //===--- BlackHole.swift --------------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2021 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | import _NumericsShims 13 | 14 | @_transparent 15 | public func blackHole(_ thing: T) { 16 | withUnsafePointer(to: thing) { 17 | _numerics_optimization_barrier($0) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Tests/RealTests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #[[ 2 | This source file is part of the Swift Numerics open source project 3 | 4 | Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See https://swift.org/LICENSE.txt for license information 8 | #]] 9 | 10 | add_library(RealTests 11 | ApproximateEqualityTests.swift 12 | AugmentedArithmeticTests.swift 13 | ElementaryFunctionChecks.swift 14 | IntegerExponentTests.swift 15 | RelaxedArithmeticTests.swift) 16 | target_compile_options(RealTests PRIVATE 17 | -enable-testing) 18 | target_link_libraries(RealTests PUBLIC 19 | $<$>:Foundation> 20 | RealModule 21 | _TestSupport 22 | XCTest) 23 | -------------------------------------------------------------------------------- /Sources/IntegerUtilities/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #[[ 2 | This source file is part of the Swift Numerics open source project 3 | 4 | Copyright (c) 2019-2021 Apple Inc. and the Swift Numerics project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See https://swift.org/LICENSE.txt for license information 8 | #]] 9 | 10 | add_library(IntegerUtilities 11 | DivideWithRounding.swift 12 | GCD.swift 13 | Rotate.swift 14 | RoundingRule.swift 15 | SaturatingArithmetic.swift 16 | ShiftWithRounding.swift) 17 | set_target_properties(IntegerUtilities PROPERTIES 18 | INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) 19 | 20 | _install_target(IntegerUtilities) 21 | set_property(GLOBAL APPEND PROPERTY SWIFT_NUMERICS_EXPORTS IntegerUtilities) 22 | -------------------------------------------------------------------------------- /Tests/IntegerUtilitiesTests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #[[ 2 | This source file is part of the Swift Numerics open source project 3 | 4 | Copyright (c) 2019-2021 Apple Inc. and the Swift Numerics project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See https://swift.org/LICENSE.txt for license information 8 | #]] 9 | 10 | add_library(IntegerUtilitiesTests 11 | DivideTests.swift 12 | DoubleWidthTests.swift 13 | GCDTests.swift 14 | RotateTests.swift 15 | SaturatingArithmeticTests.swift 16 | ShiftTests.swift) 17 | target_compile_options(IntegerUtilitiesTests PRIVATE 18 | -enable-testing) 19 | target_link_libraries(IntegerUtilitiesTests PUBLIC 20 | $<$>:Foundation> 21 | Numerics 22 | _TestSupport 23 | XCTest) 24 | -------------------------------------------------------------------------------- /Sources/_TestSupport/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #[[ 2 | This source file is part of the Swift Numerics open source project 3 | 4 | Copyright (c) 2019-2021 Apple Inc. and the Swift Numerics project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See https://swift.org/LICENSE.txt for license information 8 | #]] 9 | 10 | add_library(_TestSupport 11 | BlackHole.swift 12 | DoubleWidth.swift 13 | Error.swift 14 | Interval.swift 15 | RealTestSupport.swift) 16 | set_target_properties(_TestSupport PROPERTIES 17 | INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) 18 | target_link_libraries(_TestSupport PUBLIC 19 | Numerics) 20 | 21 | 22 | _install_target(_TestSupport) 23 | set_property(GLOBAL APPEND PROPERTY SWIFT_NUMERICS_EXPORTS _TestSupport) 24 | -------------------------------------------------------------------------------- /Sources/ComplexModule/Complex+IntegerLiteral.swift: -------------------------------------------------------------------------------- 1 | //===--- Complex+IntegerLiteral.swift -------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | extension Complex: ExpressibleByIntegerLiteral { 13 | public typealias IntegerLiteralType = RealType.IntegerLiteralType 14 | 15 | @inlinable 16 | public init(integerLiteral value: IntegerLiteralType) { 17 | self.init(RealType(integerLiteral: value)) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Tests/ComplexTests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #[[ 2 | This source file is part of the Swift Numerics open source project 3 | 4 | Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See https://swift.org/LICENSE.txt for license information 8 | #]] 9 | 10 | add_library(ComplexTests 11 | ApproximateEqualityTests.swift 12 | ArithmeticTests.swift 13 | ElementaryFunctionTests.swift 14 | PropertyTests.swift) 15 | set_target_properties(ComplexTests PROPERTIES 16 | INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) 17 | target_compile_options(ComplexTests PRIVATE 18 | -enable-testing) 19 | target_link_libraries(ComplexTests PUBLIC 20 | $<$>:Foundation> 21 | ComplexModule 22 | Numerics 23 | _TestSupport 24 | XCTest) 25 | -------------------------------------------------------------------------------- /Sources/_NumericsShims/_NumericsShims.c: -------------------------------------------------------------------------------- 1 | //===--- NumericsShims.c --------------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | // This file exists only to trigger the NumericShims module to build; without 13 | // it swiftpm won't build anything, and then the shims are not available for 14 | // the modules that need them. 15 | 16 | // If any shims are added that are not pure header inlines, whatever runtime 17 | // support they require can be added to this file. 18 | 19 | #include "_NumericsShims.h" 20 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | name: Pull request 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | pull_request: 8 | types: [opened, reopened, synchronize] 9 | 10 | jobs: 11 | tests: 12 | name: Test 13 | uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main 14 | with: 15 | enable_macos_checks: true 16 | windows_exclude_swift_versions: '[{"swift_version": "5.9"}]' 17 | enable_wasm_sdk_build: true 18 | wasm_sdk_build_command: swift build --target Numerics 19 | soundness: 20 | name: Soundness 21 | uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main 22 | with: 23 | license_header_check_project_name: "Swift Numerics" 24 | # https://github.com/apple/swift-numerics/issues/303 25 | license_header_check_enabled: false 26 | # https://github.com/apple/swift-numerics/issues/302 27 | format_check_enabled: false 28 | -------------------------------------------------------------------------------- /Sources/RealModule/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #[[ 2 | This source file is part of the Swift Numerics open source project 3 | 4 | Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See https://swift.org/LICENSE.txt for license information 8 | #]] 9 | 10 | add_library(RealModule 11 | AlgebraicField.swift 12 | ApproximateEquality.swift 13 | AugmentedArithmetic.swift 14 | Double+Real.swift 15 | ElementaryFunctions.swift 16 | Float+Real.swift 17 | Float16+Real.swift 18 | Float80+Real.swift 19 | Real.swift 20 | RealFunctions.swift 21 | RelaxedArithmetic.swift) 22 | set_target_properties(RealModule PROPERTIES 23 | INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) 24 | target_link_libraries(RealModule PUBLIC 25 | _NumericsShims) 26 | 27 | 28 | _install_target(RealModule) 29 | set_property(GLOBAL APPEND PROPERTY SWIFT_NUMERICS_EXPORTS RealModule) 30 | -------------------------------------------------------------------------------- /Sources/ComplexModule/Complex+StringConvertible.swift: -------------------------------------------------------------------------------- 1 | //===--- Complex+StringConvertible.swift ----------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | extension Complex: CustomStringConvertible { 13 | public var description: String { 14 | guard isFinite else { return "inf" } 15 | return "(\(x), \(y))" 16 | } 17 | } 18 | 19 | #if compiler(>=6.0) 20 | @_unavailableInEmbedded 21 | #endif 22 | extension Complex: CustomDebugStringConvertible { 23 | public var debugDescription: String { 24 | "Complex<\(RealType.self)>(\(String(reflecting: x)), \(String(reflecting: y)))" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/ComplexModule/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #[[ 2 | This source file is part of the Swift Numerics open source project 3 | 4 | Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See https://swift.org/LICENSE.txt for license information 8 | #]] 9 | 10 | add_library(ComplexModule 11 | Complex.swift 12 | Complex+AdditiveArithmetic.swift 13 | Complex+AlgebraicField.swift 14 | Complex+Codable.swift 15 | Complex+ElementaryFunctions.swift 16 | Complex+Hashable.swift 17 | Complex+IntegerLiteral.swift 18 | Complex+Numeric.swift 19 | Complex+StringConvertible.swift 20 | Polar.swift 21 | Scale.swift) 22 | set_target_properties(ComplexModule PROPERTIES 23 | INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) 24 | target_link_libraries(ComplexModule PUBLIC 25 | RealModule) 26 | 27 | 28 | _install_target(ComplexModule) 29 | set_property(GLOBAL APPEND PROPERTY SWIFT_NUMERICS_EXPORTS ComplexModule) 30 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #[[ 2 | This source file is part of the Swift Numerics open source project 3 | 4 | Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See https://swift.org/LICENSE.txt for license information 8 | #]] 9 | 10 | cmake_minimum_required(VERSION 3.16) 11 | project(swift-numerics 12 | LANGUAGES Swift) 13 | 14 | list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules) 15 | 16 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 17 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 18 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 19 | set(CMAKE_Swift_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/swift) 20 | 21 | include(CTest) 22 | include(SwiftSupport) 23 | 24 | add_subdirectory(Sources) 25 | 26 | get_property(SWIFT_NUMERICS_EXPORTS GLOBAL PROPERTY SWIFT_NUMERICS_EXPORTS) 27 | export(TARGETS ${SWIFT_NUMERICS_EXPORTS} 28 | NAMESPACE SwiftNumerics:: 29 | FILE swift-numerics-config.cmake 30 | EXPORT_LINK_INTERFACE_LIBRARIES) 31 | -------------------------------------------------------------------------------- /Sources/Numerics/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #[[ 2 | This source file is part of the Swift Numerics open source project 3 | 4 | Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See https://swift.org/LICENSE.txt for license information 8 | #]] 9 | 10 | add_library(Numerics 11 | Numerics.swift) 12 | set_target_properties(Numerics PROPERTIES 13 | INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) 14 | # NOTE: generate the force load symbol to ensure that the import library is 15 | # generated on Windows for autolinking. 16 | target_compile_options(Numerics PUBLIC 17 | $<$>:-autolink-force-load> 18 | # SR-12254: workaround for the swift compiler not properly tracking the force 19 | # load symbol when validating the TBD 20 | -Xfrontend -validate-tbd-against-ir=none) 21 | target_link_libraries(Numerics PUBLIC 22 | ComplexModule 23 | IntegerUtilities 24 | RealModule) 25 | 26 | 27 | _install_target(Numerics) 28 | set_property(GLOBAL APPEND PROPERTY SWIFT_NUMERICS_EXPORTS Numerics) 29 | -------------------------------------------------------------------------------- /Sources/_TestSupport/RealTestSupport.swift: -------------------------------------------------------------------------------- 1 | //===--- RealTestSupport.swift --------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019-2020 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | import RealModule 13 | 14 | public protocol FixedWidthFloatingPoint: BinaryFloatingPoint 15 | where Exponent: FixedWidthInteger, 16 | RawSignificand: FixedWidthInteger { } 17 | 18 | #if !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64)) 19 | @available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) 20 | extension Float16: FixedWidthFloatingPoint { } 21 | #endif 22 | 23 | extension Float: FixedWidthFloatingPoint { } 24 | extension Double: FixedWidthFloatingPoint { } 25 | #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android) 26 | extension Float80: FixedWidthFloatingPoint { } 27 | #endif 28 | 29 | extension FloatingPointSign { 30 | static func random(using g: inout G) -> FloatingPointSign { 31 | [.plus,.minus].randomElement(using: &g)! 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/ComplexModule/Complex+AdditiveArithmetic.swift: -------------------------------------------------------------------------------- 1 | //===--- Complex+AdditiveArithmetic.swift ---------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | import RealModule 13 | 14 | extension Complex: AdditiveArithmetic { 15 | /// The additive identity, with real and imaginary parts both zero. 16 | /// 17 | /// See also: ``one``, ``i``, ``infinity`` 18 | @_transparent 19 | public static var zero: Complex { 20 | Complex(0, 0) 21 | } 22 | 23 | @_transparent 24 | public static func +(z: Complex, w: Complex) -> Complex { 25 | return Complex(z.x + w.x, z.y + w.y) 26 | } 27 | 28 | @_transparent 29 | public static func -(z: Complex, w: Complex) -> Complex { 30 | return Complex(z.x - w.x, z.y - w.y) 31 | } 32 | 33 | @_transparent 34 | public static func +=(z: inout Complex, w: Complex) { 35 | z = z + w 36 | } 37 | 38 | @_transparent 39 | public static func -=(z: inout Complex, w: Complex) { 40 | z = z - w 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/ComplexModule/Complex+Codable.swift: -------------------------------------------------------------------------------- 1 | //===--- Complex+Codable.swift --------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | import RealModule 13 | 14 | // FloatingPoint does not refine Codable, so this is a conditional conformance. 15 | #if compiler(>=6.0) 16 | @_unavailableInEmbedded 17 | #endif 18 | extension Complex: Decodable where RealType: Decodable { 19 | public init(from decoder: Decoder) throws { 20 | var unkeyedContainer = try decoder.unkeyedContainer() 21 | let x = try unkeyedContainer.decode(RealType.self) 22 | let y = try unkeyedContainer.decode(RealType.self) 23 | self.init(x, y) 24 | } 25 | } 26 | 27 | #if compiler(>=6.0) 28 | @_unavailableInEmbedded 29 | #endif 30 | extension Complex: Encodable where RealType: Encodable { 31 | public func encode(to encoder: Encoder) throws { 32 | var unkeyedContainer = encoder.unkeyedContainer() 33 | try unkeyedContainer.encode(x) 34 | try unkeyedContainer.encode(y) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/_TestSupport/Error.swift: -------------------------------------------------------------------------------- 1 | //===--- Error.swift ------------------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2020 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import ComplexModule 14 | import RealModule 15 | 16 | public func relativeError(_ tst: Float, _ ref: Double) -> Double { 17 | let scale = max(ref.magnitude, Double(Float.leastNormalMagnitude)) 18 | let error = (Double(tst) - ref).magnitude 19 | return error / scale 20 | } 21 | 22 | public func componentwiseError(_ tst: Complex, _ ref: Complex) -> Double { 23 | return max(relativeError(tst.real, ref.real), 24 | relativeError(tst.imaginary, ref.imaginary)) 25 | } 26 | 27 | public func relativeError(_ tst: Complex, _ ref: Complex) -> Double { 28 | let scale = max(ref.magnitude, Double(Float.leastNormalMagnitude)) 29 | let dtst = Complex(Double(tst.real), Double(tst.imaginary)) 30 | let error = (dtst - ref).magnitude 31 | return error / scale 32 | } 33 | -------------------------------------------------------------------------------- /Sources/RealModule/Documentation.docc/RealModule.md: -------------------------------------------------------------------------------- 1 | # ``RealModule`` 2 | 3 | Extensions on the Swift standard library that provide functionality for 4 | floating-point types. 5 | 6 | ## Overview 7 | 8 | ``RealModule`` provides four protocols that extend the standard library's 9 | numeric protocol hierarchy: AlgebraicField, ElementaryFunctions, 10 | RealFunctions, and Real. 11 | 12 | Types conforming to AlgebraicField represent 13 | [fields](https://en.wikipedia.org/wiki/Field_(mathematics)). These are the 14 | mathematical structures that typically form the elements of vectors and 15 | matrices, so this protocol is appropriate for writing generic code to do 16 | linear-algebra-type operations. 17 | 18 | ElementaryFunctions provides bindings for the "math functions": the logarithm 19 | and exponential functions, sine, cosine and tangent as well as their inverses, 20 | and other functions that you may be familiar with from trigonometry and 21 | calculus. RealFunctions refines ElementaryFunctions and provides functions that 22 | are primarily used with the real numbers, such as atan2, erf and gamma, and 23 | the base-2 and -10 logarithm and exponential funtions. 24 | 25 | The Real protocol is a convenient name for the intersection of `FloatingPoint`, 26 | `RealFunctions`, and `AlgebraicField`; this is the protocol that you are most 27 | likely to want to constrain to when writing generic "math" code that works 28 | with floating-point types. 29 | -------------------------------------------------------------------------------- /Sources/RealModule/Documentation.docc/Augmented.md: -------------------------------------------------------------------------------- 1 | # ``Augmented`` 2 | 3 | A namespace for a family of algorithms that compute the results of floating- 4 | point arithmetic using multiple values such that either the error is minimized 5 | or the result is exact. 6 | 7 | ## Overview 8 | 9 | Consider multiplying two Doubles. A Double has 53 significand bits, so their 10 | product could be up to 106 bits wide before it is rounded to a Double result. 11 | So up to 53 of those 106 bits will be "lost" in that process: 12 | 13 | ```swift 14 | let a = 1.0 + .ulpOfOne // 1 + 2⁻⁵² 15 | let b = 1.0 - .ulpOfOne // 1 - 2⁻⁵² 16 | let c = a * b // 1 - 2⁻¹⁰⁴ before rounding, rounds to 1.0 17 | ``` 18 | 19 | Sometimes it is necessary to preserve some or all of those low-order bits; 20 | maybe a subsequent subtraction cancels most of the high-order bits, and so 21 | the low-order part of the product suddenly becomes significant: 22 | 23 | ```swift 24 | let result = 1 - c // exactly zero, but "should be" 2⁻¹⁰⁴ 25 | ``` 26 | 27 | Augmented arithmetic is a building-block that library writers can use to 28 | handle cases like this more carefully. For the example above, one might 29 | compute: 30 | 31 | ```swift 32 | let (head, tail) = Augmented.product(a,b) 33 | ``` 34 | 35 | `head` is then 1.0 and `tail` is -2⁻¹⁰⁴, so no information has been lost. 36 | Of course, the result is now split across two Doubles instead of one, but the 37 | information in `tail` can be carried forward into future computations. 38 | -------------------------------------------------------------------------------- /Sources/_TestSupport/Interval.swift: -------------------------------------------------------------------------------- 1 | //===--- Interval.swift ---------------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2020 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | // A not-particularly-clever floating-point iterval that is iterable for the 14 | // purposes of testing. 15 | public struct Interval: Sequence where Element: FloatingPoint { 16 | 17 | let lower: Element 18 | 19 | let upper: Element 20 | 21 | public init(from: Element, through: Element) { 22 | precondition(from <= through) 23 | lower = from 24 | upper = through 25 | } 26 | 27 | public init(from: Element, to: Element) { 28 | self.init(from: from, through: to.nextDown) 29 | } 30 | 31 | public func makeIterator() -> Iterator { 32 | Iterator(self) 33 | } 34 | 35 | public struct Iterator: IteratorProtocol { 36 | let interval: Interval 37 | var nextOutput: Element? 38 | 39 | init(_ interval: Interval) { 40 | self.interval = interval 41 | self.nextOutput = interval.lower 42 | } 43 | 44 | public mutating func next() -> Element? { 45 | let result = nextOutput 46 | if nextOutput == interval.upper { nextOutput = nil } 47 | else { nextOutput = nextOutput?.nextUp } 48 | return result 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/ComplexModule/Scale.swift: -------------------------------------------------------------------------------- 1 | //===--- Scale.swift ------------------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | // Policy: deliberately not using the * and / operators for these at the 13 | // moment, because then there's an ambiguity in expressions like 2*z; is 14 | // that Complex(2) * z or is it RealType(2) * z? This is especially 15 | // problematic in type inference: suppose we have: 16 | // 17 | // let a: RealType = 1 18 | // let b = 2*a 19 | // 20 | // what is the type of b? If we don't have a type context, it's ambiguous. 21 | // If we have a Complex type context, then b will be inferred to have type 22 | // Complex! Obviously, that doesn't help anyone. 23 | 24 | extension Complex { 25 | /// The result of multiplying this value by the real number `a`. 26 | /// 27 | /// Equivalent to `self * Complex(a)`, but may be computed more efficiently. 28 | @inlinable @inline(__always) 29 | public func multiplied(by a: RealType) -> Complex { 30 | Complex(x*a, y*a) 31 | } 32 | 33 | /// The result of dividing this value by the real number `a`. 34 | /// 35 | /// More efficient than `self / Complex(a)`. May not produce exactly the 36 | /// same result, but will always be more accurate if they differ. 37 | @inlinable @inline(__always) 38 | public func divided(by a: RealType) -> Complex { 39 | Complex(x/a, y/a) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Tests/IntegerUtilitiesTests/GCDTests.swift: -------------------------------------------------------------------------------- 1 | //===--- GCDTests.swift ---------------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2021 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import IntegerUtilities 14 | import XCTest 15 | 16 | final class IntegerUtilitiesGCDTests: XCTestCase { 17 | func testGCDInt() { 18 | XCTAssertEqual(gcd(0, 0), 0) 19 | XCTAssertEqual(gcd(0, 1), 1) 20 | XCTAssertEqual(gcd(1, 0), 1) 21 | XCTAssertEqual(gcd(0, -1), 1) 22 | XCTAssertEqual(gcd(1, 1), 1) 23 | XCTAssertEqual(gcd(1, 2), 1) 24 | XCTAssertEqual(gcd(2, 2), 2) 25 | XCTAssertEqual(gcd(4, 2), 2) 26 | XCTAssertEqual(gcd(6, 8), 2) 27 | XCTAssertEqual(gcd(77, 91), 7) 28 | XCTAssertEqual(gcd(24, -36), 12) 29 | XCTAssertEqual(gcd(-24, -36), 12) 30 | XCTAssertEqual(gcd(51, 34), 17) 31 | XCTAssertEqual(gcd(64, 96), 32) 32 | XCTAssertEqual(gcd(-64, 96), 32) 33 | XCTAssertEqual(gcd(4*7*19, 27*25), 1) 34 | XCTAssertEqual(gcd(16*315, 11*315), 315) 35 | XCTAssertEqual(gcd(97*67*53*27*8, 83*67*53*9*32), 67*53*9*8) 36 | XCTAssertEqual(gcd(Int.min, 2), 2) 37 | 38 | // TODO: Enable these when version compatibility allows. 39 | // 40 | // XCTExpectFailure{ gcd(0, Int.min) } 41 | // XCTExpectFailure{ gcd(Int.min, 0) } 42 | // XCTExpectFailure{ gcd(Int.min, Int.min) } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/ComplexModule/Documentation.docc/Complex.md: -------------------------------------------------------------------------------- 1 | # ``Complex`` 2 | 3 | A complex number type represented by its real and imaginary parts, and equipped 4 | with the usual arithmetic operators and math functions. 5 | 6 | ## Overview 7 | 8 | You can access these Cartesian components using the real and imaginary 9 | properties. 10 | 11 | ```swift 12 | let z = Complex(1,-1) // 1 - i 13 | let re = z.real // 1 14 | let im = z.imaginary // -1 15 | ``` 16 | 17 | All `Complex` numbers with a non-finite component are treated as a single 18 | "point at infinity," with infinite magnitude and indeterminant phase. Thus, 19 | the real and imaginary parts of an infinity are nan. 20 | 21 | ```swift 22 | let w = Complex.infinity 23 | w == -w // true 24 | let re = w.real // .nan 25 | let im = w.imag // .nan 26 | ``` 27 | 28 | See for more details. 29 | 30 | The ``magnitude`` property of a complex number is the infinity norm of the 31 | value (a.k.a. “maximum norm” or “Чебышёв [Chebyshev] norm”). To get the two 32 | norm (a.k.a. "Euclidean norm"), use the ``length`` property. See 33 | for more details. 34 | 35 | ## Topics 36 | 37 | ### Real and imaginary parts 38 | 39 | - ``real`` 40 | - ``imaginary`` 41 | - ``rawStorage`` 42 | - ``init(_:_:)`` 43 | - ``init(_:)-5aesj`` 44 | - ``init(imaginary:)`` 45 | 46 | ### Phase, length and magnitude 47 | 48 | - ``magnitude`` 49 | - ``length`` 50 | - ``lengthSquared`` 51 | - ``normalized`` 52 | - ``phase`` 53 | - ``polar`` 54 | - ``init(length:phase:)`` 55 | 56 | ### Scaling by real numbers 57 | - ``multiplied(by:)`` 58 | - ``divided(by:)`` 59 | 60 | ### Complex-specific operations 61 | - ``conjugate`` 62 | 63 | ### Classification 64 | - ``isZero`` 65 | - ``isSubnormal`` 66 | - ``isNormal`` 67 | - ``isFinite`` 68 | -------------------------------------------------------------------------------- /Sources/ComplexModule/Complex+Hashable.swift: -------------------------------------------------------------------------------- 1 | //===--- Complex+Hashable.swift -------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | import RealModule 13 | 14 | extension Complex: Hashable { 15 | @_transparent 16 | public static func ==(a: Complex, b: Complex) -> Bool { 17 | // Identify all numbers with either component non-finite as a single 18 | // "point at infinity". 19 | guard a.isFinite || b.isFinite else { return true } 20 | // For finite numbers, equality is defined componentwise. Cases where 21 | // only one of a or b is infinite fall through to here as well, but this 22 | // expression correctly returns false for them so we don't need to handle 23 | // them explicitly. 24 | return a.x == b.x && a.y == b.y 25 | } 26 | 27 | @_transparent 28 | public func hash(into hasher: inout Hasher) { 29 | // There are two equivalence classes to which we owe special attention: 30 | // All zeros should hash to the same value, regardless of sign, and all 31 | // non-finite numbers should hash to the same value, regardless of 32 | // representation. The correct behavior for zero falls out for free from 33 | // the hash behavior of floating-point, but we need to use a 34 | // representative member for any non-finite values. 35 | if isFinite { 36 | hasher.combine(x) 37 | hasher.combine(y) 38 | } else { 39 | hasher.combine(RealType.infinity) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/IntegerUtilities/GCD.swift: -------------------------------------------------------------------------------- 1 | //===--- GCD.swift --------------------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2021-2024 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | /// The [greatest common divisor][gcd] of `a` and `b`. 13 | /// 14 | /// If both inputs are zero, the result is zero. If one input is zero, the 15 | /// result is the absolute value of the other input. 16 | /// 17 | /// The result must be representable within its type. In particular, the gcd 18 | /// of a signed, fixed-width integer type's minimum with itself (or zero) 19 | /// cannot be represented, and results in a trap. 20 | /// 21 | /// gcd(Int.min, Int.min) // Overflow error 22 | /// gcd(Int.min, 0) // Overflow error 23 | /// 24 | /// [gcd]: https://en.wikipedia.org/wiki/Greatest_common_divisor 25 | @inlinable 26 | public func gcd(_ a: T, _ b: T) -> T { 27 | var x = a 28 | var y = b 29 | if x.magnitude < y.magnitude { swap(&x, &y) } 30 | // Avoid overflow when x = signed min, y = -1. 31 | if y.magnitude == 1 { return 1 } 32 | // Euclidean algorithm for GCD. It's worth using Lehmer instead for larger 33 | // integer types, but for now this is good and dead-simple and faster than 34 | // the other obvious choice, the binary algorithm. 35 | while y != 0 { (x, y) = (y, x%y) } 36 | // Try to convert result to T. 37 | if let result = T(exactly: x.magnitude) { return result } 38 | // If that fails, produce a diagnostic. 39 | fatalError("GCD (\(x)) is not representable as \(T.self).") 40 | } 41 | -------------------------------------------------------------------------------- /Sources/IntegerUtilities/Rotate.swift: -------------------------------------------------------------------------------- 1 | //===--- Rotate.swift -----------------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2021 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | extension FixedWidthInteger { 13 | @_transparent @usableFromInline 14 | internal func rotateImplementation(right count: Int) -> Self { 15 | // We don't have an unsigned right shift operation for signed values, so 16 | // we need to convert to an unsigned type. The only unsigned type that's 17 | // guaranteed to be able to represent the bit pattern of any Self value 18 | // is Magnitude. It would be possible to have Magnitude be _wider_ than 19 | // Self, but that's OK as long as we're careful to complement the shift 20 | // count using Self.bitWidth and not Magnitude.bitWidth or zero. 21 | let bitPattern = Magnitude(truncatingIfNeeded: self) 22 | let countComplement = Self.bitWidth &- count 23 | return Self(truncatingIfNeeded: 24 | bitPattern &>> count | bitPattern &<< countComplement 25 | ) 26 | } 27 | 28 | /// `self` rotated bitwise right by `count` bits. 29 | /// 30 | /// Equivalent to `rotated(left: 0 &- count)`. 31 | @inlinable 32 | public func rotated(right count: Count) -> Self { 33 | rotateImplementation(right: Int(truncatingIfNeeded: count)) 34 | } 35 | 36 | /// `self` rotated bitwise left by `count` bits. 37 | /// 38 | /// Equivalent to `rotated(right: 0 &- count)`. 39 | @inlinable 40 | public func rotated(left count: Count) -> Self { 41 | rotateImplementation(right: 0 &- Int(truncatingIfNeeded: count)) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/RealModule/Documentation.docc/ElementaryFunctions.md: -------------------------------------------------------------------------------- 1 | # ``ElementaryFunctions`` 2 | 3 | A type that has elementary functions (`sin`, `cos`, etc.) available. 4 | 5 | ## Overview 6 | 7 | An ["elementary function"][elfn] is a function built up from powers, roots, 8 | exponentials, logarithms, trigonometric functions (sin, cos, tan) and 9 | their inverses, and the hyperbolic functions (sinh, cosh, tanh) and their 10 | inverses. 11 | 12 | Conformance to this protocol means that all of these building blocks are 13 | available as static functions on the type. 14 | 15 | ```swift 16 | let x: Float = 1 17 | let y = Float.sin(x) // 0.84147096 18 | ``` 19 | 20 | `ElementaryFunctions` conformance implies `AdditiveArithmetic`, so addition 21 | and subtraction and the `zero` property are also available. 22 | 23 | ``RealFunctions`` refines this protocol and adds additional functions that 24 | are primarily used with real numbers, such as ``RealFunctions/atan2(y:x:)`` 25 | and ``RealFunctions/exp10(_:)``. 26 | 27 | ``Real`` conforms to `RealFunctions` and `FloatingPoint`, and is the 28 | protocol that you will use most often for generic code. 29 | 30 | ## Topics 31 | 32 | There are a few families of functions defined by `ElementaryFunctions`: 33 | 34 | ### Exponential functions 35 | - ``exp(_:)`` 36 | - ``expMinusOne(_:)`` 37 | 38 | ### Logarithmetic functions 39 | - ``log(_:)`` 40 | - ``log(onePlus:)`` 41 | 42 | ### Power and root functions: 43 | - ``pow(_:_:)-9imp6`` 44 | - ``pow(_:_:)-2qmul`` 45 | - ``sqrt(_:)`` 46 | - ``root(_:_:)`` 47 | 48 | ### Trigonometric functions 49 | - ``cos(_:)`` 50 | - ``sin(_:)`` 51 | - ``tan(_:)`` 52 | 53 | ### Inverse trigonometric functions 54 | - ``acos(_:)`` 55 | - ``asin(_:)`` 56 | - ``atan(_:)`` 57 | 58 | ### Hyperbolic functions 59 | - ``cosh(_:)`` 60 | - ``sinh(_:)`` 61 | - ``tanh(_:)`` 62 | 63 | ### Inverse hyperbolic functions 64 | - ``acosh(_:)`` 65 | - ``asinh(_:)`` 66 | - ``atanh(_:)`` 67 | 68 | [elfn]: http://en.wikipedia.org/wiki/Elementary_function 69 | -------------------------------------------------------------------------------- /Sources/RealModule/Documentation.docc/Relaxed.md: -------------------------------------------------------------------------------- 1 | # ``Relaxed`` 2 | 3 | A namespace for a family of operations that "relax" the usual rules for 4 | floating-point to allow reassociation of arithmetic and FMA formation. 5 | 6 | ## Overview 7 | 8 | Because of rounding, and the arithmetic rules for infinity and NaN values, 9 | floating-point addition and multiplication are not associative: 10 | 11 | ```swift 12 | let ε = Double.leastNormalMagnitude 13 | let sumLeft = (-1 + 1) + ε // 0 + ε = ε 14 | let sumRight = -1 + (1 + ε) // -1 + 1 = 0 15 | 16 | let ∞ = Double.infinity 17 | let productLeft = (ε * ε) * ∞ // 0 * ∞ = .nan 18 | let productRight = ε * (ε * ∞) // ε * ∞ = ∞ 19 | ``` 20 | 21 | For some algorithms, the distinction between these results is incidental; for 22 | some others it is critical to their correct function. Because of this, 23 | compilers cannot freely change the order of reductions, which prevents some 24 | important optimizations: extraction of instruction-level parallelism and 25 | vectorization. 26 | 27 | If you know that you are in a case where the order of elements being summed 28 | or multiplied is incidental, the Relaxed operations give you a mechanism 29 | to communicate that to the compiler and unlock these optimizations. For 30 | example, consider the following two functions: 31 | 32 | ```swift 33 | func sum(array: [Float]) -> Float { 34 | array.reduce(0, +) 35 | } 36 | 37 | func relaxedSum(array: [Float]) -> Float { 38 | array.reduce(0, Relaxed.sum) 39 | } 40 | ``` 41 | 42 | when called on an array with 1000 elements in a Release build, `relaxedSum` 43 | is about 8x faster than `sum` on Apple M2, with a similar speedup on Intel 44 | processors, without the need for any unsafe code or flags. 45 | 46 | ### multiplyAdd 47 | 48 | In addition to `sum` and `product`, `Relaxed` provides the 49 | ``multiplyAdd(_:_:_:)`` operation, which communciates to the compiler that 50 | it is allowed to replace separate multiply and add operations with a single 51 | _fused multiply-add_ instruction if its cost model indicates that it would 52 | be advantageous to do so. When targeting processors that support this 53 | instruction, this may be a significant performance advantage. 54 | -------------------------------------------------------------------------------- /Sources/ComplexModule/Complex+Numeric.swift: -------------------------------------------------------------------------------- 1 | //===--- Complex+Numeric.swift --------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | extension Complex: Numeric { 13 | 14 | @_transparent 15 | public static func *(z: Complex, w: Complex) -> Complex { 16 | return Complex(z.x*w.x - z.y*w.y, z.x*w.y + z.y*w.x) 17 | } 18 | 19 | @_transparent 20 | public static func *=(z: inout Complex, w: Complex) { 21 | z = z * w 22 | } 23 | 24 | /// The complex number with specified real part and zero imaginary part. 25 | /// 26 | /// Equivalent to `Complex(RealType(real), 0)`. 27 | @inlinable 28 | public init(_ real: Other) { 29 | self.init(RealType(real), 0) 30 | } 31 | 32 | /// The complex number with specified real part and zero imaginary part, 33 | /// if it can be constructed without rounding. 34 | @inlinable 35 | public init?(exactly real: Other) { 36 | guard let real = RealType(exactly: real) else { return nil } 37 | self.init(real, 0) 38 | } 39 | 40 | /// The infinity-norm of the value (a.k.a. "maximum norm" or "Чебышёв 41 | /// [Chebyshev] norm"). 42 | /// 43 | /// Equal to `max(abs(real), abs(imaginary))`. 44 | /// 45 | /// If you need to work with the Euclidean norm (a.k.a. 2-norm) instead, 46 | /// use the ``length`` or ``lengthSquared`` properties. If you just need 47 | /// to know "how big" a number is, use this property. 48 | /// 49 | /// **Edge cases:** 50 | /// 51 | /// - If `z` is not finite, `z.magnitude` is infinity. 52 | /// - If `z` is zero, `z.magnitude` is zero. 53 | /// - Otherwise, `z.magnitude` is finite and non-zero. 54 | @_transparent 55 | public var magnitude: RealType { 56 | guard isFinite else { return .infinity } 57 | return max(abs(x), abs(y)) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.9 2 | //===--- Package.swift ----------------------------------------*- swift -*-===// 3 | // 4 | // This source file is part of the Swift Numerics open source project 5 | // 6 | // Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors 7 | // Licensed under Apache License v2.0 with Runtime Library Exception 8 | // 9 | // See https://swift.org/LICENSE.txt for license information 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import PackageDescription 14 | 15 | let excludedFilenames = ["CMakeLists.txt", "README.md"] 16 | 17 | let package = Package( 18 | 19 | name: "swift-numerics", 20 | products: [ 21 | .library(name: "ComplexModule", targets: ["ComplexModule"]), 22 | .library(name: "Numerics", targets: ["Numerics"]), 23 | .library(name: "RealModule", targets: ["RealModule"]), 24 | ], 25 | 26 | targets: [ 27 | // MARK: - Public API 28 | .target( 29 | name: "ComplexModule", 30 | dependencies: ["RealModule"], 31 | exclude: excludedFilenames 32 | ), 33 | 34 | .target( 35 | name: "IntegerUtilities", 36 | dependencies: [], 37 | exclude: excludedFilenames 38 | ), 39 | 40 | .target( 41 | name: "Numerics", 42 | dependencies: ["ComplexModule", "IntegerUtilities", "RealModule"], 43 | exclude: excludedFilenames 44 | ), 45 | 46 | .target( 47 | name: "RealModule", 48 | dependencies: ["_NumericsShims"], 49 | exclude: excludedFilenames, 50 | linkerSettings: [ 51 | .linkedLibrary("m", .when(platforms: [.linux, .android])) 52 | ] 53 | ), 54 | 55 | // MARK: - Implementation details 56 | .target( 57 | name: "_NumericsShims", 58 | exclude: excludedFilenames 59 | ), 60 | 61 | .target( 62 | name: "_TestSupport", 63 | dependencies: ["Numerics"], 64 | exclude: ["CMakeLists.txt"] 65 | ), 66 | 67 | // MARK: - Unit test bundles 68 | .testTarget( 69 | name: "ComplexTests", 70 | dependencies: ["_TestSupport"], 71 | exclude: ["CMakeLists.txt"] 72 | ), 73 | 74 | .testTarget( 75 | name: "IntegerUtilitiesTests", 76 | dependencies: ["IntegerUtilities", "_TestSupport"], 77 | exclude: ["CMakeLists.txt"] 78 | ), 79 | 80 | .testTarget( 81 | name: "RealTests", 82 | dependencies: ["_TestSupport"], 83 | exclude: ["CMakeLists.txt"] 84 | ) 85 | ] 86 | ) 87 | -------------------------------------------------------------------------------- /Sources/ComplexModule/Documentation.docc/Infinity.md: -------------------------------------------------------------------------------- 1 | # Zero and infinity 2 | 3 | Semantics of `Complex` zero and infinity values, and important considerations 4 | when porting code from other languages. 5 | 6 | ## Overview 7 | 8 | Unlike C and C++'s complex types, `Complex` does not attempt to make a 9 | semantic distinction between different infinity and NaN values. Any `Complex` 10 | datum with a non-finite component is treated as the "point at infinity" on 11 | the Riemann sphere--a value with infinite magnitude and unspecified phase. 12 | 13 | As a consequence, all values with either component infinite or NaN compare 14 | equal, and hash the same. Similarly, all zero values compare equal and hash 15 | the same. 16 | 17 | ### Rationale 18 | 19 | This choice has some drawbacks,¹ but also some significant advantages. 20 | In particular, complex multiplication is the most common operation performed 21 | with a complex type, and one would like to be able to use the usual naive 22 | arithmetic implementation, consisting of four real multiplications and two 23 | real additions: 24 | 25 | ``` 26 | (a + bi) * (c + di) = (ac - bd) + (ad + bc)i 27 | ``` 28 | 29 | `Complex` can use this implementation, because we do not differentiate between 30 | infinities and NaN values. C and C++, by contrast, cannot use this 31 | implementation by default, because, for example: 32 | 33 | ``` 34 | (1 + ∞i) * (0 - 2i) = (1*0 - ∞*(-2)) + (1*(-2) + ∞*0)i 35 | = (0 - ∞) + (-2 + nan)i 36 | = -∞ + nan i 37 | ``` 38 | 39 | `Complex` treats this as "infinity", which is the correct result. C and C++ 40 | treat it as a nan value, however, which is incorrect; infinity multiplied 41 | by a non-zero number should be infinity. Thus, C and C++ (by default) must 42 | detect these special cases and fix them up, which makes multiplication a 43 | more computationally expensive operation.² 44 | 45 | ### Footnotes: 46 | ¹ W. Kahan, Branch Cuts for Complex Elementary Functions, or Much Ado 47 | About Nothing's Sign Bit. In A. Iserles and M.J.D. Powell, editors, 48 | _Proceedings The State of Art in Numerical Analysis_, pages 165–211, 1987. 49 | 50 | ² This can be addressed in C programs by use of the `STDC CX_LIMITED_RANGE` 51 | pragma, which instructs the compiler to simply not care about these cases. 52 | Unfortunately, this pragma is not often used in real C or C++ programs 53 | (though it does see some use in _libraries_). Programmers tend to specify 54 | `-ffast-math` or maybe `-ffinite-math-only` instead, which has other 55 | undesirable consequences. 56 | -------------------------------------------------------------------------------- /Tests/ComplexTests/ApproximateEqualityTests.swift: -------------------------------------------------------------------------------- 1 | //===--- ApproximateEqualityTests.swift -----------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2020 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import Numerics 14 | import XCTest 15 | 16 | final class ApproximateEqualityTests: XCTestCase { 17 | 18 | func testSpecials(absolute tol: T) { 19 | let zero = Complex.zero 20 | let inf = Complex.infinity 21 | XCTAssertTrue(zero.isApproximatelyEqual(to: zero, absoluteTolerance: tol)) 22 | XCTAssertTrue(zero.isApproximatelyEqual(to:-zero, absoluteTolerance: tol)) 23 | XCTAssertTrue((-zero).isApproximatelyEqual(to: zero, absoluteTolerance: tol)) 24 | XCTAssertTrue((-zero).isApproximatelyEqual(to:-zero, absoluteTolerance: tol)) 25 | // Complex has a single point at infinity. 26 | XCTAssertTrue(inf.isApproximatelyEqual(to: inf, absoluteTolerance: tol)) 27 | XCTAssertTrue(inf.isApproximatelyEqual(to:-inf, absoluteTolerance: tol)) 28 | XCTAssertTrue((-inf).isApproximatelyEqual(to: inf, absoluteTolerance: tol)) 29 | XCTAssertTrue((-inf).isApproximatelyEqual(to:-inf, absoluteTolerance: tol)) 30 | } 31 | 32 | func testSpecials(relative tol: T) { 33 | let zero = Complex.zero 34 | let inf = Complex.infinity 35 | XCTAssertTrue(zero.isApproximatelyEqual(to: zero, relativeTolerance: tol)) 36 | XCTAssertTrue(zero.isApproximatelyEqual(to:-zero, relativeTolerance: tol)) 37 | XCTAssertTrue((-zero).isApproximatelyEqual(to: zero, relativeTolerance: tol)) 38 | XCTAssertTrue((-zero).isApproximatelyEqual(to:-zero, relativeTolerance: tol)) 39 | // Complex has a single point at infinity. 40 | XCTAssertTrue(inf.isApproximatelyEqual(to: inf, relativeTolerance: tol)) 41 | XCTAssertTrue(inf.isApproximatelyEqual(to:-inf, relativeTolerance: tol)) 42 | XCTAssertTrue((-inf).isApproximatelyEqual(to: inf, relativeTolerance: tol)) 43 | XCTAssertTrue((-inf).isApproximatelyEqual(to:-inf, relativeTolerance: tol)) 44 | } 45 | 46 | func testSpecials(_ type: T.Type) { 47 | XCTAssertTrue(Complex.zero.isApproximatelyEqual(to: .zero)) 48 | XCTAssertTrue(Complex.zero.isApproximatelyEqual(to:-.zero)) 49 | testSpecials(absolute: T.zero) 50 | testSpecials(absolute: T.greatestFiniteMagnitude) 51 | testSpecials(relative: T.ulpOfOne) 52 | testSpecials(relative: T(1)) 53 | } 54 | 55 | func testFloat() { 56 | testSpecials(Float.self) 57 | } 58 | 59 | func testDouble() { 60 | testSpecials(Double.self) 61 | } 62 | 63 | #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android) 64 | func testFloat80() { 65 | testSpecials(Float80.self) 66 | } 67 | #endif 68 | } 69 | -------------------------------------------------------------------------------- /Sources/ComplexModule/Documentation.docc/ComplexModule.md: -------------------------------------------------------------------------------- 1 | # ``ComplexModule`` 2 | 3 | Types and operations for working with complex numbers. 4 | 5 | ## Overview 6 | 7 | ### Representation 8 | 9 | The `Complex` type is generic over an associated `RealType`; complex numbers 10 | are represented as two `RealType` values, the real and imaginary parts of the 11 | number. 12 | 13 | ``` 14 | let z = Complex(1, 2) 15 | let re = z.real 16 | let im = z.imaginary 17 | ``` 18 | 19 | ### Memory layout 20 | 21 | A `Complex` value is stored as two `RealType` values arranged consecutively 22 | in memory. Thus it has the same memory layout as: 23 | - A Fortran complex value built on the corresponding real type (as used 24 | by BLAS and LAPACK). 25 | - A C struct with real and imaginary parts and nothing else (as used by 26 | computational libraries predating C99). 27 | - A C99 `_Complex` value built on the corresponding real type. 28 | - A C++ `std::complex` value built on the corresponding real type. 29 | Functions taking complex arguments in these other languages are not 30 | automatically converted on import, but you can safely write shims that 31 | map them into Swift types by converting pointers. 32 | 33 | ### Real-Complex arithmetic 34 | 35 | Because the real numbers are a subset of the complex numbers, many 36 | languages support arithmetic with mixed real and complex operands. 37 | For example, C allows the following: 38 | 39 | ```c 40 | #include 41 | double r = 1; 42 | double complex z = CMPLX(0, 2); // 2i 43 | double complex w = r + z; // 1 + 2i 44 | ``` 45 | 46 | The `Complex` type does not provide such mixed operators: 47 | 48 | ```swift 49 | let r = 1.0 50 | let z = Complex(imaginary: 2.0) 51 | let w = r + z // error: binary operator '+' cannot be applied to operands of type 'Double' and 'Complex' 52 | ``` 53 | 54 | In order to write the example from C above in Swift, you have to perform an 55 | explicit conversion: 56 | 57 | ```swift 58 | let r = 1.0 59 | let z = Complex(imaginary: 2.0) 60 | let w = Complex(r) + z // OK 61 | ``` 62 | 63 | There are two reasons for this choice. Most importantly, Swift generally avoids 64 | mixed-type arithmetic. Second, if we _did_ provide such heterogeneous operators, 65 | it would lead to undesirable behavior in common expressions when combined with 66 | literal type inference. Consider the following example: 67 | 68 | ```swift 69 | let a: Double = 1 70 | let b = 2*a 71 | ``` 72 | 73 | `b` ought to have type `Double`, but if we did have a Complex-by-Real `*` 74 | operation, `2*a` would either be ambiguous (if there were no type context), 75 | or be inferred to have type `Complex` (if the expression appeared 76 | in the context of an extension defined on `Complex`). 77 | 78 | Note that we _do_ provide heterogeneous multiplication and division by a real 79 | value, spelled as ``Complex/divided(by:)`` and ``Complex/multiplied(by:)`` 80 | to avoid ambiguity. 81 | 82 | ```swift 83 | let z = Complex(1,3) 84 | let w = z.multiplied(by: 2) 85 | ``` 86 | 87 | These operations are generally more efficient than converting the scale to 88 | a complex number and then using `*` or `/`. 89 | -------------------------------------------------------------------------------- /Sources/IntegerUtilities/README.md: -------------------------------------------------------------------------------- 1 | # Integer Utilities 2 | 3 | _Note: This module is only present on `main`, and has not yet been stabilized and released in a tag._ 4 | 5 | ## Utilities defined for `BinaryInteger` 6 | 7 | The following API are defined for all integer types: 8 | 9 | - The `gcd(_:_:)` free function implements the _Greatest Common Divisor_ operation. 10 | 11 | - The `shifted(rightBy:rounding:)` method implements _bitwise shift with rounding_. 12 | 13 | - The `divided(by:rounding:)` method implements division with specified rounding. 14 | (See also `SignedInteger.divided(by:rounding:)`, `remainder(dividingBy:rounding:)`, and `euclideanDivision(_:_:)` below). 15 | 16 | ## Utilities defined for `SignedInteger` 17 | 18 | The following API are defined for signed integer types: 19 | 20 | - The `divided(by:rounding:)` method implementing division with specified rounding, returning both quotient and remainder. 21 | This requires a signed type because the remainder is not generally representable for unsigned types. 22 | This is a disfavored overload; by default, you will get only the quotient as the result: 23 | ``` 24 | let p = 5.divided(by: 3, rounding: .up) // p = 2 25 | let (q, r) = 5.divided(by: 3, rounding: .up) // q = 2, r = -1 26 | ``` 27 | 28 | - The `remainder(dividingBy:rounding:)` method implementing the remainder operation; the `rounding` argument describes how to round the _quotient_, which is not returned. 29 | (The remainder is always exact, and hence is not rounded). 30 | 31 | - The `euclideanDivision(_:_:)` free function implements _Euclidean division_. 32 | In this operation, the remainder is chosen to always be non-negative. 33 | This does not correspond to any rounding rule on the quotient, which is why it uses a distinct API. 34 | 35 | ## Utilities defined for `FixedWidthInteger` 36 | 37 | - The `rotated(right:)` and `rotated(left:)` methods implement _bitwise rotation_ for signed and unsigned integer types. 38 | The count parameter may be any `BinaryInteger` type. 39 | 40 | ### [Saturating Arithmetic][saturating] 41 | 42 | The following saturating operations are defined as methods on `FixedWidthInteger`: 43 | 44 | - `addingWithSaturation(_:)` 45 | - `subtractingWithSaturation(_:)` 46 | - `negatedWithSaturation(_:)` 47 | - `multipliedWithSaturation(by:)` 48 | - `shiftedWithSaturation(leftBy:rounding:)` 49 | 50 | These implement _saturating arithmetic_. 51 | They are an alternative to the usual `+`, `-`, and `*` operators, which trap if the result cannot be represented in the argument type, and `&+`, `&-`, `&*`, and `<<`, which wrap out-of-range results modulo 2ⁿ for some n. 52 | Instead these methods clamp the result to the representable range of the type: 53 | ``` 54 | let x: Int8 = 84 55 | let y: Int8 = 100 56 | let a = x + y // traps due to overflow 57 | let b = x &+ y // wraps to -72 58 | let c = x.addingWithSaturation(y) // saturates to 127 59 | ``` 60 | 61 | If you are using saturating arithmetic, you may also want to perform saturating conversions between integer types; this functionality is provided by the standard library via the [`init(clamping:)` API][clamping]. 62 | 63 | ## Types 64 | 65 | The `RoundingRule` enum is used with shift, division, and round operations to specify how to round their results to a representable value. 66 | 67 | [saturating]: https://en.wikipedia.org/wiki/Saturation_arithmetic 68 | [clamping]: https://developer.apple.com/documentation/swift/binaryinteger/init(clamping:) 69 | -------------------------------------------------------------------------------- /Sources/RealModule/RelaxedArithmetic.swift: -------------------------------------------------------------------------------- 1 | //===--- RelaxedArithmetic.swift ------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2021-2025 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | import _NumericsShims 13 | 14 | public enum Relaxed { } 15 | 16 | extension Relaxed { 17 | /// a+b, but grants the optimizer permission to reassociate expressions 18 | /// and form FMAs. 19 | /// 20 | /// Floating-point addition is not an associative operation, so the Swift 21 | /// compiler does not have any flexibility in how it evaluates an expression 22 | /// like: 23 | /// ``` 24 | /// func sum(array: [Float]) -> Float { 25 | /// array.reduce(0, +) 26 | /// } 27 | /// ``` 28 | /// Using `Relaxed.sum` instead of `+` permits the compiler to reorder the 29 | /// terms in the summation, which unlocks loop unrolling and vectorization. 30 | /// In a benchmark, simply using `Relaxed.sum` provides about an 8x speedup 31 | /// for release builds, without any unsafe flags or other optimizations. 32 | /// Further improvement should be possible by improving LLVM optimizations 33 | /// or adding attributes to license more aggressive unrolling and taking 34 | /// advantage of vector ISA extensions for swift. 35 | /// 36 | /// If you want to compute `a-b` with relaxed semantics, use 37 | /// `Relaxed.sum(a, -b)`. 38 | /// 39 | /// If a type or toolchain does not support reassociation for optimization 40 | /// purposes, this operation decays to a normal addition; it is a license 41 | /// for the compiler to optimize, not a guarantee that any change occurs. 42 | @_transparent 43 | public static func sum(_ a: T, _ b: T) -> T { 44 | T._relaxedAdd(a, b) 45 | } 46 | 47 | /// a*b, but grants the optimizer permission to reassociate expressions 48 | /// and form FMAs. 49 | /// 50 | /// Floating-point addition and multiplication are not associative operations, 51 | /// so the Swift compiler does not have any flexibility in how it evaluates 52 | /// an expression like: 53 | /// ``` 54 | /// func sumOfSquares(array: [Float]) -> Float { 55 | /// array.reduce(0) { $0 + $1*$1 } 56 | /// } 57 | /// ``` 58 | /// Using `Relaxed.sum` and `Relaxed.product` instead of `+` and `*` permits 59 | /// the compiler to reorder the terms in the summation, which unlocks loop 60 | /// unrolling and vectorization, and form fused multiply-adds, which allows 61 | /// us to achieve twice the throughput on some hardware. 62 | /// 63 | /// If a type or toolchain does not support reassociation for optimization 64 | /// purposes, this operation decays to a normal multiplication; it is a 65 | /// license for the compiler to optimize, not a guarantee that any change 66 | /// occurs. 67 | @_transparent 68 | public static func product(_ a: T, _ b: T) -> T { 69 | T._relaxedMul(a, b) 70 | } 71 | } 72 | 73 | extension Relaxed { 74 | /// a*b + c, computed _either_ with an FMA or with separate multiply and add, 75 | /// whichever is fastest according to the optimizer's heuristics. 76 | @_transparent 77 | public static func multiplyAdd( 78 | _ a: T, _ b: T, _ c: T 79 | ) -> T { 80 | T._relaxedAdd(c, T._relaxedMul(a, b)) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Sources/RealModule/RealFunctions.swift: -------------------------------------------------------------------------------- 1 | //===--- RealFunctions.swift ----------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | public protocol RealFunctions: ElementaryFunctions { 13 | /// The signed angle formed in the plane between the vector `(x,y)` and the 14 | /// positive real axis, measured in radians. 15 | /// 16 | /// The result is in the interval `[-π, π]`. 17 | /// 18 | /// The argument order to `atan2` may be surprising to new programmers. 19 | /// The convention of `y` being the first argument goes back at least to 20 | /// Fortran IV in 1961 and is generally followed in computing with a few 21 | /// notable exceptions (e.g. Mathematica and Excel). This convention was 22 | /// originally chosen because of the mathematical definition of the 23 | /// function: 24 | /// 25 | /// ``` 26 | /// atan2(y,x) = atan(y/x) if x > 0 27 | /// ``` 28 | /// 29 | /// See also ``ElementaryFunctions/atan(_:)``, as well as the `phase` and 30 | /// `polar` properties defined on the `Complex` type. 31 | static func atan2(y: Self, x: Self) -> Self 32 | 33 | /// The [error function](https://en.wikipedia.org/wiki/Error_function) 34 | /// evaluated at `x`. 35 | static func erf(_ x: Self) -> Self 36 | 37 | /// The complimentary [error function](https://en.wikipedia.org/wiki/Error_function) 38 | /// evaluated at `x`. 39 | static func erfc(_ x: Self) -> Self 40 | 41 | /// 2 raised to the power x. 42 | /// 43 | /// See also ``log2(_:)``, ``ElementaryFunctions/exp(_:)``, 44 | /// ``ElementaryFunctions/expMinusOne(_:)`` 45 | /// and ``ElementaryFunctions/pow(_:_:)-2qmul``. 46 | static func exp2(_ x: Self) -> Self 47 | 48 | /// 10 raised to the power x. 49 | /// 50 | /// See also ``log10(_:)``, ``ElementaryFunctions/exp(_:)``, 51 | /// ``ElementaryFunctions/expMinusOne(_:)`` 52 | /// and ``ElementaryFunctions/pow(_:_:)-2qmul``. 53 | static func exp10(_ x: Self) -> Self 54 | 55 | /// The length of the vector `(x,y)`, computed in a manner that avoids 56 | /// spurious overflow or underflow. 57 | /// 58 | /// See also the `length` and `polar` properties defined on the `Complex` 59 | /// type. 60 | static func hypot(_ x: Self, _ y: Self) -> Self 61 | 62 | /// The [gamma function](https://en.wikipedia.org/wiki/Gamma_function) Γ(x). 63 | static func gamma(_ x: Self) -> Self 64 | 65 | /// The base-2 logarithm of `x`. 66 | /// 67 | /// See also ``exp2(_:)``, ``ElementaryFunctions/log(_:)``, 68 | /// and ``ElementaryFunctions/log(onePlus:)``. 69 | static func log2(_ x: Self) -> Self 70 | 71 | /// The base-10 logarithm of `x`. 72 | /// 73 | /// See also ``exp10(_:)``, ``ElementaryFunctions/log(_:)``, 74 | /// and ``ElementaryFunctions/log(onePlus:)``. 75 | static func log10(_ x: Self) -> Self 76 | 77 | #if !os(Windows) 78 | /// The logarithm of the absolute value of the 79 | /// [gamma function](https://en.wikipedia.org/wiki/Gamma_function), 80 | /// log(|Γ(x)|). 81 | /// 82 | /// Not available on Windows targets. 83 | static func logGamma(_ x: Self) -> Self 84 | 85 | /// The sign of the 86 | /// [gamma function](https://en.wikipedia.org/wiki/Gamma_function), Γ(x). 87 | /// 88 | /// For `x >= 0`, `signGamma(x)` is `.plus`. For negative `x`, `signGamma(x)` 89 | /// is `.plus` when `x` is an integer, and otherwise it is `.minus` whenever 90 | /// `trunc(x)` is even, and `.plus` when `trunc(x)` is odd. 91 | /// 92 | /// This function is used together with ``logGamma(_:)``, which computes the 93 | /// logarithm of the absolute value of Γ(x), to recover the sign information. 94 | /// 95 | /// Not available on Windows targets. 96 | static func signGamma(_ x: Self) -> FloatingPointSign 97 | #endif 98 | } 99 | -------------------------------------------------------------------------------- /cmake/modules/SwiftSupport.cmake: -------------------------------------------------------------------------------- 1 | #[[ 2 | This source file is part of the Swift Numerics open source project 3 | 4 | Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See https://swift.org/LICENSE.txt for license information 8 | #]] 9 | 10 | # Returns the architecture name in a variable 11 | # 12 | # Usage: 13 | # get_swift_host_arch(result_var_name) 14 | # 15 | # Sets ${result_var_name} with the converted architecture name derived from 16 | # CMAKE_SYSTEM_PROCESSOR. 17 | function(get_swift_host_arch result_var_name) 18 | if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") 19 | set("${result_var_name}" "x86_64" PARENT_SCOPE) 20 | elseif ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "AArch64|aarch64|arm64") 21 | if(CMAKE_SYSTEM_NAME MATCHES Darwin) 22 | set("${result_var_name}" "arm64" PARENT_SCOPE) 23 | else() 24 | set("${result_var_name}" "aarch64" PARENT_SCOPE) 25 | endif() 26 | elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "ppc64") 27 | set("${result_var_name}" "powerpc64" PARENT_SCOPE) 28 | elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "ppc64le") 29 | set("${result_var_name}" "powerpc64le" PARENT_SCOPE) 30 | elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "s390x") 31 | set("${result_var_name}" "s390x" PARENT_SCOPE) 32 | elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv6l") 33 | set("${result_var_name}" "armv6" PARENT_SCOPE) 34 | elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv7l") 35 | set("${result_var_name}" "armv7" PARENT_SCOPE) 36 | elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv7-a") 37 | set("${result_var_name}" "armv7" PARENT_SCOPE) 38 | elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64") 39 | set("${result_var_name}" "x86_64" PARENT_SCOPE) 40 | elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "IA64") 41 | set("${result_var_name}" "itanium" PARENT_SCOPE) 42 | elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86") 43 | set("${result_var_name}" "i686" PARENT_SCOPE) 44 | elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "i686") 45 | set("${result_var_name}" "i686" PARENT_SCOPE) 46 | elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "riscv64") 47 | set("${result_var_name}" "riscv64" PARENT_SCOPE) 48 | else() 49 | message(FATAL_ERROR "Unrecognized architecture on host system: ${CMAKE_SYSTEM_PROCESSOR}") 50 | endif() 51 | endfunction() 52 | 53 | # Returns the os name in a variable 54 | # 55 | # Usage: 56 | # get_swift_host_os(result_var_name) 57 | # 58 | # 59 | # Sets ${result_var_name} with the converted OS name derived from 60 | # CMAKE_SYSTEM_NAME. 61 | function(get_swift_host_os result_var_name) 62 | if(CMAKE_SYSTEM_NAME STREQUAL Darwin) 63 | set(${result_var_name} macosx PARENT_SCOPE) 64 | else() 65 | string(TOLOWER ${CMAKE_SYSTEM_NAME} cmake_system_name_lc) 66 | set(${result_var_name} ${cmake_system_name_lc} PARENT_SCOPE) 67 | endif() 68 | endfunction() 69 | 70 | function(_install_target module) 71 | get_swift_host_arch(swift_arch) 72 | get_swift_host_os(swift_os) 73 | install(TARGETS ${module} 74 | ARCHIVE DESTINATION lib/swift$<$>:_static>/${swift_os} 75 | LIBRARY DESTINATION lib/swift$<$>:_static>/${swift_os} 76 | RUNTIME DESTINATION bin) 77 | if(CMAKE_SYSTEM_NAME STREQUAL Darwin) 78 | install(FILES $/${module}.swiftdoc 79 | DESTINATION lib/swift$<$>:_static>/${swift_os}/${mmodule}.swiftmodule 80 | RENAME ${swift_arch}.swiftdoc) 81 | install(FILES $/${module}.swiftmodule 82 | DESTINATION lib/swift$<$>:_static>/${swift_os}/${mmodule}.swiftmodule 83 | RENAME ${swift_arch}.swiftmodule) 84 | else() 85 | install(FILES 86 | $/${module}.swiftdoc 87 | $/${module}.swiftmodule 88 | DESTINATION lib/swift$<$>:_static>/${swift_os}/${swift_arch}) 89 | endif() 90 | endfunction() 91 | -------------------------------------------------------------------------------- /Sources/RealModule/README.md: -------------------------------------------------------------------------------- 1 | # Real Module 2 | 3 | [SE-0246] proposed an API for "basic math functions" that would make operations like sine and logarithm available in generic contexts. 4 | It was accepted, but because of limitations in the compiler, the API could not be added to the standard library in a source-stable manner. 5 | `RealModule` provides that API as a separate module so that you can use it right away to get access to the improved API for these operations in your projects. 6 | 7 | ## Protocols and Methods 8 | 9 | The module defines four protocols. The most general is `ElementaryFunctions`, which makes the following functions available: 10 | - Exponential functions: `exp`, `expMinusOne` 11 | - Logarithmic functions: `log`, `log(onePlus:)` 12 | - Trigonometric functions: `cos`, `sin`, `tan` 13 | - Inverse trigonometric functions: `acos`, `asin`, `atan` 14 | - Hyperbolic functions: `cosh`, `sinh`, `tanh` 15 | - Inverse hyperbolic functions: `acosh`, `asinh`, `atanh` 16 | - Power and root functions: `pow`, `sqrt`, `root` 17 | 18 | `ElementaryFunctions` refines `AdditiveArithmetic`, and so also provides addition, subtraction, and the `.zero` property. 19 | 20 | The `RealFunctions` protocol refines `ElementaryFunctions`, and adds operations that are difficult to define or implement over fields more general than the real numbers: 21 | - `atan2(y:x:)`, which computes `atan(y/x)` with sign chosen by the quadrant of the point `(x,y)` in the Cartesian plane. 22 | - `hypot`, which computes `sqrt(x*x + y*y)` without intermediate overflow or underflow. 23 | - `erf` and `erfc`, the [error function][ErrorFunction] and its complement. 24 | - Exponential functions: `exp2` and `exp10` 25 | - Logarithmetic functions: `log2` and `log10` 26 | - Gamma functions: `gamma`, `logGamma`, and `signGamma`, which evaluate the [gamma function][GammaFunction], its logarithm, and its sign. 27 | 28 | The protocol that you will use most often is `Real`, which describes a floating-point type equipped with the full set of basic math functions. 29 | This is a great protocol to use in writing generic code, because it has all the basics that you need to implement most numeric functions. 30 | 31 | The fourth protocol is `AlgebraicField`, which `Real` also refines. 32 | This protocol is a very small refinement of `SignedNumeric`, adding the `/` and `/=` operators and a `reciprocal` property. 33 | The primary use of this protocol is for writing code that is generic over real and complex types. 34 | 35 | ## Using Real 36 | 37 | First, either import `RealModule` directly or import the `Numerics` umbrella module. 38 | 39 | Suppose we were experimenting with some basic machine learning, and needed a generic [sigmoid function][Sigmoid] activation function: 40 | 41 | ```swift 42 | import Numerics 43 | 44 | func sigmoid(_ x: T) -> T { 45 | 1 / (1 + .exp(-x)) 46 | } 47 | ``` 48 | 49 | Or suppose we were implementing a DFT, and wanted to precompute weights for the transform; DFT weights are roots of unity: 50 | 51 | ```swift 52 | import Numerics 53 | 54 | extension Real { 55 | // The real and imaginary parts of e^{-2πik/n} 56 | static func dftWeight(k: Int, n: Int) -> (r: Self, i: Self) { 57 | precondition(0 <= k && k < n, "k is out of range") 58 | guard let N = Self(exactly: n) else { 59 | preconditionFailure("n cannot be represented exactly.") 60 | } 61 | let theta = -2 * .pi * (Self(k) / N) 62 | return (r: .cos(theta), i: .sin(theta)) 63 | } 64 | } 65 | ``` 66 | 67 | This gives us an implementation that works for `Float`, `Double`, and `Float80` if the target supports it. 68 | When new basic floating-point types are added to Swift, like `Float16` or `Float128`, it will work for them as well. 69 | Not having this protocol is a significant missing feature for numerical computing in Swift, and I'm really looking forward to seeing what people do with it. 70 | 71 | ### Dependencies: 72 | - The C standard math library (`libm`) via the `_NumericsShims` target. 73 | 74 | [ErrorFunction]: https://en.wikipedia.org/wiki/Error_function 75 | [GammaFunction]: https://en.wikipedia.org/wiki/Gamma_function 76 | [SE-0246]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0246-mathable.md 77 | [Sigmoid]: https://en.wikipedia.org/wiki/Sigmoid_function 78 | -------------------------------------------------------------------------------- /Tests/IntegerUtilitiesTests/RotateTests.swift: -------------------------------------------------------------------------------- 1 | //===--- RotateTests.swift ------------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2021 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import IntegerUtilities 14 | import XCTest 15 | 16 | final class IntegerUtilitiesRotateTests: XCTestCase { 17 | func testRotateUInt8() { 18 | let x: UInt8 = 0b10011100 19 | XCTAssertEqual(0b01001110, x.rotated(right:-7)) 20 | XCTAssertEqual(0b00100111, x.rotated(right:-6)) 21 | XCTAssertEqual(0b10010011, x.rotated(right:-5)) 22 | XCTAssertEqual(0b11001001, x.rotated(right:-4)) 23 | XCTAssertEqual(0b11100100, x.rotated(right:-3)) 24 | XCTAssertEqual(0b01110010, x.rotated(right:-2)) 25 | XCTAssertEqual(0b00111001, x.rotated(right:-1)) 26 | XCTAssertEqual(0b10011100, x.rotated(right: 0)) 27 | XCTAssertEqual(0b01001110, x.rotated(right: 1)) 28 | XCTAssertEqual(0b00100111, x.rotated(right: 2)) 29 | XCTAssertEqual(0b10010011, x.rotated(right: 3)) 30 | XCTAssertEqual(0b11001001, x.rotated(right: 4)) 31 | XCTAssertEqual(0b11100100, x.rotated(right: 5)) 32 | XCTAssertEqual(0b01110010, x.rotated(right: 6)) 33 | XCTAssertEqual(0b00111001, x.rotated(right: 7)) 34 | XCTAssertEqual(0b10011100, x.rotated(right: 8)) 35 | } 36 | 37 | func testRotateInt16() { 38 | let x = Int16(bitPattern: 0b1001110000111110) 39 | XCTAssertEqual(Int16(bitPattern: 0b1001110000111110), x.rotated(left:-16)) 40 | XCTAssertEqual(Int16(bitPattern: 0b0011100001111101), x.rotated(left:-15)) 41 | XCTAssertEqual(Int16(bitPattern: 0b0111000011111010), x.rotated(left:-14)) 42 | XCTAssertEqual(Int16(bitPattern: 0b1110000111110100), x.rotated(left:-13)) 43 | XCTAssertEqual(Int16(bitPattern: 0b1100001111101001), x.rotated(left:-12)) 44 | XCTAssertEqual(Int16(bitPattern: 0b1000011111010011), x.rotated(left:-11)) 45 | XCTAssertEqual(Int16(bitPattern: 0b0000111110100111), x.rotated(left:-10)) 46 | XCTAssertEqual(Int16(bitPattern: 0b0001111101001110), x.rotated(left:-9)) 47 | XCTAssertEqual(Int16(bitPattern: 0b0011111010011100), x.rotated(left:-8)) 48 | XCTAssertEqual(Int16(bitPattern: 0b0111110100111000), x.rotated(left:-7)) 49 | XCTAssertEqual(Int16(bitPattern: 0b1111101001110000), x.rotated(left:-6)) 50 | XCTAssertEqual(Int16(bitPattern: 0b1111010011100001), x.rotated(left:-5)) 51 | XCTAssertEqual(Int16(bitPattern: 0b1110100111000011), x.rotated(left:-4)) 52 | XCTAssertEqual(Int16(bitPattern: 0b1101001110000111), x.rotated(left:-3)) 53 | XCTAssertEqual(Int16(bitPattern: 0b1010011100001111), x.rotated(left:-2)) 54 | XCTAssertEqual(Int16(bitPattern: 0b0100111000011111), x.rotated(left:-1)) 55 | XCTAssertEqual(Int16(bitPattern: 0b1001110000111110), x.rotated(left: 0)) 56 | XCTAssertEqual(Int16(bitPattern: 0b0011100001111101), x.rotated(left: 1)) 57 | XCTAssertEqual(Int16(bitPattern: 0b0111000011111010), x.rotated(left: 2)) 58 | XCTAssertEqual(Int16(bitPattern: 0b1110000111110100), x.rotated(left: 3)) 59 | XCTAssertEqual(Int16(bitPattern: 0b1100001111101001), x.rotated(left: 4)) 60 | XCTAssertEqual(Int16(bitPattern: 0b1000011111010011), x.rotated(left: 5)) 61 | XCTAssertEqual(Int16(bitPattern: 0b0000111110100111), x.rotated(left: 6)) 62 | XCTAssertEqual(Int16(bitPattern: 0b0001111101001110), x.rotated(left: 7)) 63 | XCTAssertEqual(Int16(bitPattern: 0b0011111010011100), x.rotated(left: 8)) 64 | XCTAssertEqual(Int16(bitPattern: 0b0111110100111000), x.rotated(left: 9)) 65 | XCTAssertEqual(Int16(bitPattern: 0b1111101001110000), x.rotated(left: 10)) 66 | XCTAssertEqual(Int16(bitPattern: 0b1111010011100001), x.rotated(left: 11)) 67 | XCTAssertEqual(Int16(bitPattern: 0b1110100111000011), x.rotated(left: 12)) 68 | XCTAssertEqual(Int16(bitPattern: 0b1101001110000111), x.rotated(left: 13)) 69 | XCTAssertEqual(Int16(bitPattern: 0b1010011100001111), x.rotated(left: 14)) 70 | XCTAssertEqual(Int16(bitPattern: 0b0100111000011111), x.rotated(left: 15)) 71 | XCTAssertEqual(Int16(bitPattern: 0b1001110000111110), x.rotated(left: 16)) 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /Tests/RealTests/RelaxedArithmeticTests.swift: -------------------------------------------------------------------------------- 1 | //===--- RelaxedArithmeticTests.swift -------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2021 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | import XCTest 13 | import RealModule 14 | import _TestSupport 15 | #if canImport(Accelerate) 16 | import Accelerate 17 | #endif 18 | 19 | #if !DEBUG 20 | func strictSum(_ array: [T]) -> T { 21 | array.reduce(0, +) 22 | } 23 | 24 | func relaxedSum(_ array: [T]) -> T { 25 | array.reduce(0, Relaxed.sum) 26 | } 27 | 28 | func strictSumOfSquares(_ array: [T]) -> T { 29 | array.reduce(0) { $0 + $1*$1 } 30 | } 31 | 32 | func relaxedSumOfSquares(_ array: [T]) -> T { 33 | array.reduce(0) { Relaxed.multiplyAdd($1, $1, $0) } 34 | } 35 | 36 | // TODO: not a great harness, but making it better bumps up against the 37 | // limitations of what XCT measure { } lets us do easily. Good enough for now. 38 | func benchmarkReduction(_ data: [Float], _ reduction: ([Float]) -> Float) { 39 | var accum: Float = 0 40 | let iters = 100_000 41 | for _ in 0 ..< iters { 42 | accum += reduction(data) 43 | } 44 | blackHole(accum) 45 | } 46 | 47 | final class RelaxedArithmeticTests: XCTestCase { 48 | 49 | var floatData: [Float] = [] 50 | 51 | override func setUp() { 52 | super.setUp() 53 | floatData = (0 ..< 1024).map { _ in .random(in: .sqrt(1/2) ..< .sqrt(2)) } 54 | } 55 | 56 | func testStrictSumPerformance() { 57 | measure { benchmarkReduction(floatData, strictSum) } 58 | } 59 | 60 | func testRelaxedSumPerformance() { 61 | // Performance of this should be closer to vDSP.sum than to 62 | // strict sum 63 | measure { benchmarkReduction(floatData, relaxedSum) } 64 | } 65 | 66 | #if canImport(Accelerate) 67 | func testvDSPSumPerformance() { 68 | measure { benchmarkReduction(floatData, vDSP.sum) } 69 | } 70 | #endif 71 | 72 | func testStrictDotPerformance() { 73 | measure { benchmarkReduction(floatData, strictSumOfSquares) } 74 | } 75 | 76 | func testRelaxedDotPerformance() { 77 | // Performance of this should be closer to vDSP.sumOfSquares than to 78 | // strict sumOfSquares 79 | measure { benchmarkReduction(floatData, relaxedSumOfSquares) } 80 | } 81 | 82 | #if canImport(Accelerate) 83 | func testvDSPDotPerformance() { 84 | measure { benchmarkReduction(floatData, vDSP.sumOfSquares) } 85 | } 86 | #endif 87 | 88 | func testRelaxedArithmetic(_ type: T.Type) { 89 | // Relaxed add is still an add; it's just permitted to reorder relative 90 | // to other adds or form FMAs. So if we do one in isolation, it has to 91 | // produce the same result as a normal addition. 92 | let a = T.random(in: -1 ... 1) 93 | let b = T.random(in: -1 ... 1) 94 | XCTAssertEqual(a + b, Relaxed.sum(a, b)) 95 | // Same is true for mul. 96 | XCTAssertEqual(a * b, Relaxed.product(a, b)) 97 | // add + mul must be either two operations or an FMA: 98 | let unfused = a + 1.5 * b 99 | let fused = a.addingProduct(1.5, b) 100 | let relaxed = Relaxed.multiplyAdd(1.5, b, a) 101 | XCTAssert(relaxed == unfused || relaxed == fused) 102 | // Summing all values in an array can be associated however we want, but 103 | // has to satisfy the usual error bound of 0.5 * sum.ulp * numberOfElements. 104 | // We don't have a golden reference, but we can compare two sums with twice 105 | // the bound for a basic check. 106 | let array = (0 ..< 128).map { _ in T.random(in: 1 ..< 2) } 107 | var ref = strictSum(array) 108 | var tst = relaxedSum(array) 109 | var bound = max(ref, tst).ulp * T(array.count) 110 | XCTAssertLessThanOrEqual(abs(ref - tst), bound) 111 | // Similarly for sum of squares ... 112 | ref = strictSumOfSquares(array) 113 | tst = relaxedSumOfSquares(array) 114 | bound = 2 * max(ref, tst).ulp * T(array.count) 115 | XCTAssertLessThanOrEqual(abs(ref - tst), bound) 116 | } 117 | 118 | func testRelaxedArithmetic() { 119 | #if !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64)) 120 | testRelaxedArithmetic(Float16.self) 121 | #endif 122 | testRelaxedArithmetic(Float.self) 123 | testRelaxedArithmetic(Double.self) 124 | #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android) 125 | testRelaxedArithmetic(Float80.self) 126 | #endif 127 | } 128 | } 129 | #endif 130 | -------------------------------------------------------------------------------- /Sources/RealModule/Float80+Real.swift: -------------------------------------------------------------------------------- 1 | //===--- Float80+Real.swift -----------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | import _NumericsShims 13 | 14 | // Restrict extension to platforms for which Float80 exists. 15 | #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android) 16 | extension Float80: Real { 17 | @_transparent 18 | public static func cos(_ x: Float80) -> Float80 { 19 | libm_cosl(x) 20 | } 21 | 22 | @_transparent 23 | public static func sin(_ x: Float80) -> Float80 { 24 | libm_sinl(x) 25 | } 26 | 27 | @_transparent 28 | public static func tan(_ x: Float80) -> Float80 { 29 | libm_tanl(x) 30 | } 31 | 32 | @_transparent 33 | public static func acos(_ x: Float80) -> Float80 { 34 | libm_acosl(x) 35 | } 36 | 37 | @_transparent 38 | public static func asin(_ x: Float80) -> Float80 { 39 | libm_asinl(x) 40 | } 41 | 42 | @_transparent 43 | public static func atan(_ x: Float80) -> Float80 { 44 | libm_atanl(x) 45 | } 46 | 47 | @_transparent 48 | public static func cosh(_ x: Float80) -> Float80 { 49 | libm_coshl(x) 50 | } 51 | 52 | @_transparent 53 | public static func sinh(_ x: Float80) -> Float80 { 54 | libm_sinhl(x) 55 | } 56 | 57 | @_transparent 58 | public static func tanh(_ x: Float80) -> Float80 { 59 | libm_tanhl(x) 60 | } 61 | 62 | @_transparent 63 | public static func acosh(_ x: Float80) -> Float80 { 64 | libm_acoshl(x) 65 | } 66 | 67 | @_transparent 68 | public static func asinh(_ x: Float80) -> Float80 { 69 | libm_asinhl(x) 70 | } 71 | 72 | @_transparent 73 | public static func atanh(_ x: Float80) -> Float80 { 74 | libm_atanhl(x) 75 | } 76 | 77 | @_transparent 78 | public static func exp(_ x: Float80) -> Float80 { 79 | libm_expl(x) 80 | } 81 | 82 | @_transparent 83 | public static func expMinusOne(_ x: Float80) -> Float80 { 84 | libm_expm1l(x) 85 | } 86 | 87 | @_transparent 88 | public static func log(_ x: Float80) -> Float80 { 89 | libm_logl(x) 90 | } 91 | 92 | @_transparent 93 | public static func log(onePlus x: Float80) -> Float80 { 94 | libm_log1pl(x) 95 | } 96 | 97 | @_transparent 98 | public static func erf(_ x: Float80) -> Float80 { 99 | libm_erfl(x) 100 | } 101 | 102 | @_transparent 103 | public static func erfc(_ x: Float80) -> Float80 { 104 | libm_erfcl(x) 105 | } 106 | 107 | @_transparent 108 | public static func exp2(_ x: Float80) -> Float80 { 109 | libm_exp2l(x) 110 | } 111 | 112 | @_transparent 113 | public static func hypot(_ x: Float80, _ y: Float80) -> Float80 { 114 | libm_hypotl(x, y) 115 | } 116 | 117 | @_transparent 118 | public static func gamma(_ x: Float80) -> Float80 { 119 | libm_tgammal(x) 120 | } 121 | 122 | @_transparent 123 | public static func log2(_ x: Float80) -> Float80 { 124 | libm_log2l(x) 125 | } 126 | 127 | @_transparent 128 | public static func log10(_ x: Float80) -> Float80 { 129 | libm_log10l(x) 130 | } 131 | 132 | @_transparent 133 | public static func pow(_ x: Float80, _ y: Float80) -> Float80 { 134 | guard x >= 0 else { return .nan } 135 | if x == 0 && y == 0 { return .nan } 136 | return libm_powl(x, y) 137 | } 138 | 139 | @_transparent 140 | public static func pow(_ x: Float80, _ n: Int) -> Float80 { 141 | // Every Int value is exactly representable as Float80, so we don't need 142 | // to do anything fancy--unlike Float and Double, we can just call the 143 | // libm pow function. 144 | libm_powl(x, Float80(n)) 145 | } 146 | 147 | @_transparent 148 | public static func root(_ x: Float80, _ n: Int) -> Float80 { 149 | guard x >= 0 || n % 2 != 0 else { return .nan } 150 | // Workaround the issue mentioned below for the specific case of n = 3 151 | // where we can fallback on cbrt. 152 | if n == 3 { return libm_cbrtl(x) } 153 | // TODO: this implementation is not quite correct, because either n or 154 | // 1/n may be not be representable as Float80. 155 | return Float80(signOf: x, magnitudeOf: libm_powl(x.magnitude, 1/Float80(n))) 156 | } 157 | 158 | @_transparent 159 | public static func atan2(y: Float80, x: Float80) -> Float80 { 160 | libm_atan2l(y, x) 161 | } 162 | 163 | @_transparent 164 | public static func logGamma(_ x: Float80) -> Float80 { 165 | var dontCare: Int32 = 0 166 | return libm_lgammal(x, &dontCare) 167 | } 168 | 169 | @_transparent 170 | public static func _relaxedAdd(_ a: Float80, _ b: Float80) -> Float80 { 171 | _numerics_relaxed_addl(a, b) 172 | } 173 | 174 | @_transparent 175 | public static func _relaxedMul(_ a: Float80, _ b: Float80) -> Float80 { 176 | _numerics_relaxed_mull(a, b) 177 | } 178 | } 179 | #endif 180 | -------------------------------------------------------------------------------- /Sources/ComplexModule/Polar.swift: -------------------------------------------------------------------------------- 1 | //===--- Polar.swift ------------------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | import RealModule 13 | 14 | extension Complex { 15 | /// The Euclidean norm (a.k.a. 2-norm). 16 | /// 17 | /// This property takes care to avoid spurious over- or underflow in 18 | /// this computation. For example: 19 | /// 20 | /// let x: Float = 3.0e+20 21 | /// let x: Float = 4.0e+20 22 | /// let naive = sqrt(x*x + y*y) // +Inf 23 | /// let careful = Complex(x, y).length // 5.0e+20 24 | /// 25 | /// Note that it *is* still possible for this property to overflow, 26 | /// because the length can be as much as sqrt(2) times larger than 27 | /// either component, and thus may not be representable in the real type. 28 | /// 29 | /// For most use cases, you can use the cheaper ``magnitude`` 30 | /// property (which computes the ∞-norm) instead, which always produces 31 | /// a representable result. See for more details. 32 | /// 33 | /// Edge cases: 34 | /// - If a complex value is not finite, its `length` is `infinity`. 35 | /// 36 | /// See also ``lengthSquared``, ``phase``, ``polar`` 37 | /// and ``init(length:phase:)``. 38 | @_transparent 39 | public var length: RealType { 40 | let naive = lengthSquared 41 | guard naive.isNormal else { return carefulLength } 42 | return .sqrt(naive) 43 | } 44 | 45 | // Internal implementation detail of ``length``, moving slow path off 46 | // of the inline function. Note that even `carefulLength` can overflow 47 | // for finite inputs, but only when the result is outside the range 48 | // of representable values. 49 | @usableFromInline 50 | internal var carefulLength: RealType { 51 | guard isFinite else { return .infinity } 52 | return .hypot(x, y) 53 | } 54 | 55 | /// The squared length `(real*real + imaginary*imaginary)`. 56 | /// 57 | /// This property is more efficient to compute than ``length``, but is 58 | /// highly prone to overflow or underflow; for finite values that are 59 | /// not well-scaled, `lengthSquared` is often either zero or 60 | /// infinity, even when `length` is a finite number. Use this property 61 | /// only when you are certain that this value is well-scaled. 62 | /// 63 | /// For many cases, ``magnitude`` can be used instead, which is similarly 64 | /// cheap to compute and always returns a representable value. 65 | /// 66 | /// Note that because of how `lengthSquared` is used, it is a primary 67 | /// design goal that it be as fast as possible. Therefore, it does not 68 | /// normalize infinities, and may return either `.infinity` or `.nan` 69 | /// for non-finite values. 70 | @_transparent 71 | public var lengthSquared: RealType { 72 | x*x + y*y 73 | } 74 | 75 | /// The phase (angle, or "argument"). 76 | /// 77 | /// - Returns: The angle (measured above the real axis) in radians. If 78 | /// the complex value is zero or infinity, the phase is not defined, 79 | /// and `nan` is returned. 80 | /// 81 | /// See also ``length``, ``polar`` and ``init(length:phase:)``. 82 | @inlinable 83 | public var phase: RealType { 84 | guard isFinite && !isZero else { return .nan } 85 | return .atan2(y: y, x: x) 86 | } 87 | 88 | /// The length and phase (or polar coordinates) of this value. 89 | /// 90 | /// Edge cases: 91 | /// - If the complex value is zero or non-finite, phase is `.nan`. 92 | /// - If the complex value is non-finite, length is `.infinity`. 93 | /// 94 | /// See also: ``length``, ``phase`` and ``init(length:phase:)``. 95 | public var polar: (length: RealType, phase: RealType) { 96 | (length, phase) 97 | } 98 | 99 | /// Creates a complex value specified with polar coordinates. 100 | /// 101 | /// Edge cases: 102 | /// - Negative lengths are interpreted as reflecting the point through the 103 | /// origin, i.e.: 104 | /// ``` 105 | /// Complex(length: -r, phase: θ) == -Complex(length: r, phase: θ) 106 | /// ``` 107 | /// - For any `θ`, even `.infinity` or `.nan`: 108 | /// ``` 109 | /// Complex(length: .zero, phase: θ) == .zero 110 | /// ``` 111 | /// - For any `θ`, even `.infinity` or `.nan`, if `r` is infinite then: 112 | /// ``` 113 | /// Complex(length: r, phase: θ) == .infinity 114 | /// ``` 115 | /// - Otherwise, `θ` must be finite, or a precondition failure occurs. 116 | /// 117 | /// See also ``length``, ``phase`` and ``polar``. 118 | @inlinable 119 | public init(length: RealType, phase: RealType) { 120 | if phase.isFinite { 121 | self = Complex(.cos(phase), .sin(phase)).multiplied(by: length) 122 | } else { 123 | precondition( 124 | length.isZero || length.isInfinite, 125 | "Either phase must be finite, or length must be zero or infinite." 126 | ) 127 | self = Complex(length) 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /Tests/ComplexTests/PropertyTests.swift: -------------------------------------------------------------------------------- 1 | //===--- PropertyTests.swift ----------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019 - 2020 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | import XCTest 13 | import ComplexModule 14 | import RealModule 15 | 16 | final class PropertyTests: XCTestCase { 17 | 18 | func testProperties(_ type: T.Type) { 19 | // The real and imaginary parts of a non-finite value should be nan. 20 | XCTAssertTrue(Complex.infinity.real.isNaN) 21 | XCTAssertTrue(Complex.infinity.imaginary.isNaN) 22 | XCTAssertTrue(Complex(.infinity, .nan).real.isNaN) 23 | XCTAssertTrue(Complex(.nan, 0).imaginary.isNaN) 24 | // The length of a non-finite value should be infinity. 25 | XCTAssertEqual(Complex.infinity.length, .infinity) 26 | XCTAssertEqual(Complex(.infinity, .nan).length, .infinity) 27 | XCTAssertEqual(Complex(.nan, 0).length, .infinity) 28 | // The phase of a non-finite value should be nan. 29 | XCTAssertTrue(Complex.infinity.phase.isNaN) 30 | XCTAssertTrue(Complex(.infinity, .nan).phase.isNaN) 31 | XCTAssertTrue(Complex(.nan, 0).phase.isNaN) 32 | // The length of a zero value should be zero. 33 | XCTAssertEqual(Complex.zero.length, .zero) 34 | XCTAssertEqual(Complex(.zero, -.zero).length, .zero) 35 | XCTAssertEqual(Complex(-.zero,-.zero).length, .zero) 36 | // The phase of a zero value should be nan. 37 | XCTAssertTrue(Complex.zero.phase.isNaN) 38 | XCTAssertTrue(Complex(.zero, -.zero).phase.isNaN) 39 | XCTAssertTrue(Complex(-.zero,-.zero).phase.isNaN) 40 | } 41 | 42 | func testProperties() { 43 | testProperties(Float.self) 44 | testProperties(Double.self) 45 | #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android) 46 | testProperties(Float80.self) 47 | #endif 48 | } 49 | 50 | func testEquatableHashable(_ type: T.Type) { 51 | // Validate that all zeros compare and hash equal, and all non-finites 52 | // do too. 53 | let zeros = [ 54 | Complex( .zero, .zero), 55 | Complex(-.zero, .zero), 56 | Complex(-.zero,-.zero), 57 | Complex( .zero,-.zero) 58 | ] 59 | for z in zeros[1...] { 60 | XCTAssertEqual(zeros[0], z) 61 | XCTAssertEqual(zeros[0].hashValue, z.hashValue) 62 | } 63 | let infs = [ 64 | Complex( .nan, .nan), 65 | Complex(-.infinity, .nan), 66 | Complex(-.ulpOfOne, .nan), 67 | Complex( .zero, .nan), 68 | Complex( .pi, .nan), 69 | Complex( .infinity, .nan), 70 | Complex( .nan, -.infinity), 71 | Complex(-.infinity,-.infinity), 72 | Complex(-.ulpOfOne,-.infinity), 73 | Complex( .zero, -.infinity), 74 | Complex( .pi, -.infinity), 75 | Complex( .infinity,-.infinity), 76 | Complex( .nan, -.ulpOfOne), 77 | Complex(-.infinity,-.ulpOfOne), 78 | Complex( .infinity,-.ulpOfOne), 79 | Complex( .nan, .zero), 80 | Complex(-.infinity, .zero), 81 | Complex( .infinity, .zero), 82 | Complex( .nan, .pi), 83 | Complex(-.infinity, .pi), 84 | Complex( .infinity, .pi), 85 | Complex( .nan, .infinity), 86 | Complex(-.infinity, .infinity), 87 | Complex(-.ulpOfOne, .infinity), 88 | Complex( .zero, .infinity), 89 | Complex( .pi, .infinity), 90 | Complex( .infinity, .infinity), 91 | ] 92 | for i in infs[1...] { 93 | XCTAssertEqual(infs[0], i) 94 | XCTAssertEqual(infs[0].hashValue, i.hashValue) 95 | } 96 | } 97 | 98 | func testEquatableHashable() { 99 | testEquatableHashable(Float.self) 100 | testEquatableHashable(Double.self) 101 | #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android) 102 | testEquatableHashable(Float80.self) 103 | #endif 104 | } 105 | 106 | func testCodable(_ type: T.Type) throws { 107 | let encoder = JSONEncoder() 108 | encoder.nonConformingFloatEncodingStrategy = .convertToString( 109 | positiveInfinity: "inf", 110 | negativeInfinity: "-inf", 111 | nan: "nan") 112 | 113 | let decoder = JSONDecoder() 114 | decoder.nonConformingFloatDecodingStrategy = .convertFromString( 115 | positiveInfinity: "inf", 116 | negativeInfinity: "-inf", 117 | nan: "nan") 118 | 119 | for expected: Complex in [.zero, .one, .i, .infinity] { 120 | let data = try encoder.encode(expected) 121 | // print("*** \(String(decoding: data, as: Unicode.UTF8.self)) ***") 122 | let actual = try decoder.decode(Complex.self, from: data) 123 | XCTAssertEqual(actual, expected) 124 | } 125 | } 126 | 127 | func testCodable() throws { 128 | try testCodable(Float32.self) 129 | try testCodable(Float64.self) 130 | // Float80 doesn't conform to Codable. 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /Sources/ComplexModule/Documentation.docc/Magnitude.md: -------------------------------------------------------------------------------- 1 | # Magnitude and norms 2 | 3 | Introduction to the concept of norm and discussion of the `Complex` type's 4 | `.magnitude` property. 5 | 6 | ## Overview 7 | 8 | In mathematics, a *norm* is a function that gives each element of a vector 9 | space a non-negative length.¹ 10 | 11 | Many different norms can be defined on the complex numbers, viewed as a 12 | vector space over the reals. All of these norms have some basic 13 | properties. If we use *‖z‖* to represent any norm of *z*, it must: 14 | - be *subadditive* (a.k.a. satisfy the triangle inequalty): 15 | ‖z + w‖ ≤ ‖z‖ + ‖w‖ for any two complex numbers z and w. 16 | - be *homogeneous* 17 | ‖az‖ = |a|‖z‖ for any real number a and complex number z. 18 | - and be *positive definite* 19 | ‖z‖ is zero if and only if z is zero. 20 | 21 | The three most commonly-used norms are: 22 | - 1-norm ("taxicab norm"): 23 | ‖x + iy‖₁ = |x| + |y| 24 | - 2-norm ("Euclidean norm"): 25 | ‖x + iy‖₂ = √(x² + y²) 26 | - ∞-norm ("maximum norm" or "Чебышёв [Chebyshev] norm")²: 27 | ‖x + iy‖ = max(|x|,|y|) 28 | 29 | > Exercise: 30 | > 1. Check that these properties hold for one of the three norms 31 | that we just defined. (Hint: write z = a+bi and w = c+di, 32 | and use the fact that the absolute value is a norm on the 33 | real numbers, and therefore has the same property). 34 | 35 | The `Complex` type gives special names to two of these norms; `length` 36 | for the 2-norm, and `magnitude` for the ∞-norm. 37 | 38 | ### Magnitude: 39 | 40 | The `Numeric` protocol requires us to choose a norm to call `magnitude`, 41 | but does not give guidance as to which one we should pick. The easiest choice 42 | might have been the Euclidean norm; it's the one with which people are most 43 | likely to be familiar. 44 | 45 | However, there are good reasons to make a different choice: 46 | - Computing the Euclidean norm requires special care to avoid spurious 47 | overflow/underflow (see implementation notes for `length` below). The 48 | naive expressions for the taxicab and maximum norm always give the best 49 | answer possible. 50 | - Even when special care is used, the Euclidean and taxicab norms are 51 | not necessarily representable. Both can be infinite even for finite 52 | numbers. 53 | 54 | ```swift 55 | let big = Double.greatestFiniteMagnitude 56 | let z = Complex(big, big) 57 | ``` 58 | 59 | The taxicab norm of `z` would be `big + big`, which overflows; the 60 | Euclidean norm would be `sqrt(2) * big`, which also overflows. 61 | But the maximum norm is always equal to the magnitude of either `real` 62 | or `imaginary`, so it is necessarily representable. 63 | - The ∞-norm is the choice of established computational libraries, like 64 | BLAS and LAPACK. 65 | 66 | For these reasons, the `magnitude` property of `Complex` binds the 67 | maximum norm: 68 | 69 | ```swift 70 | Complex(2, 3).magnitude // 3 71 | Complex(-1, 0.5).magnitude // 1 72 | ``` 73 | 74 | ### Length: 75 | 76 | The `length` property of a `Complex` value is its Euclidean norm. 77 | 78 | ```swift 79 | Complex(2, 3).length // 3.605551275463989 80 | Complex(-1, 0.5).length // 1.118033988749895 81 | ``` 82 | 83 | Aside from familiarity, the Euclidean norm has one important property 84 | that the maximum norm lacks: 85 | - it is *multiplicative*: 86 | ‖zw‖₂ = ‖z‖₂‖w‖₂ for any two complex numbers z and w. 87 | 88 | > Exercises: 89 | > 1. Find z and w that show that the maximum norm is not multiplicative. 90 | (i.e. exhibit z and w such that ‖zw‖ ≠ ‖z‖‖w‖.) 91 | > 2. Is the 1-norm multiplicative? 92 | 93 | ### Implementation notes: 94 | 95 | The `length` property takes special care to produce an accurate answer, 96 | even when the value is poorly-scaled. The naive expression for `length` 97 | would be `sqrt(x*x + y*y)`, but this can overflow or underflow even when 98 | the final result should be a finite number. 99 | 100 | ```swift 101 | // Suppose that length were implemented like this: 102 | extension Complex { 103 | var naiveLength: RealType { 104 | .sqrt(real*real + imaginary*imaginary) 105 | } 106 | } 107 | 108 | // Then taking the length of even a modestly large number: 109 | let z = Complex(1e20, 1e20) 110 | // or small number: 111 | let w = Complex(1e-24, 1e-24) 112 | // would overflow: 113 | z.naiveLength // Inf 114 | // or underflow: 115 | w.naiveLength // 0 116 | ``` 117 | 118 | Instead, `length` is implemented using a two-step algorithm. First we 119 | compute `lengthSquared`, which is `x*x + y*y`. If this is a normal 120 | number (meaning that no overflow or underflow has occured), we can safely 121 | return its square root. Otherwise, we redo the computation with a more 122 | careful computation, which avoids spurious under- or overflow: 123 | 124 | ```swift 125 | let z = Complex(1e20, 1e20) 126 | let w = Complex(1e-24, 1e-24) 127 | z.length // 1.41421358E+20 128 | w.length // 1.41421362E-24 129 | ``` 130 | 131 | ### Footnotes: 132 | 133 | ¹ Throughout this documentation, "norm" refers to a 134 | [vector norm](https://en.wikipedia.org/wiki/Norm_(mathematics)). 135 | To confuse the matter, there are several similar things also called 136 | "norm" in mathematics. The other one you are most likely to run into 137 | is the [field norm](https://en.wikipedia.org/wiki/Field_norm). 138 | 139 | Field norms are much less common than vector norms, but the C++ 140 | `std::norm` operation implements a field norm. To get the (Euclidean) 141 | vector norm in C++, use `std::abs`. 142 | 143 | ² There's no subscript-∞ in unicode, so I write the infinity norm 144 | without the usual subscript. 145 | -------------------------------------------------------------------------------- /Sources/RealModule/Float16+Real.swift: -------------------------------------------------------------------------------- 1 | //===--- Float16+Real.swift -----------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2020 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | import _NumericsShims 13 | 14 | // Float16 is only available on macOS when targeting arm64. 15 | #if !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64)) 16 | 17 | @available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) 18 | extension Float16: Real { 19 | @_transparent 20 | public static func cos(_ x: Float16) -> Float16 { 21 | Float16(.cos(Float(x))) 22 | } 23 | 24 | @_transparent 25 | public static func sin(_ x: Float16) -> Float16 { 26 | Float16(.sin(Float(x))) 27 | } 28 | 29 | @_transparent 30 | public static func tan(_ x: Float16) -> Float16 { 31 | Float16(.tan(Float(x))) 32 | } 33 | 34 | @_transparent 35 | public static func acos(_ x: Float16) -> Float16 { 36 | Float16(.acos(Float(x))) 37 | } 38 | 39 | @_transparent 40 | public static func asin(_ x: Float16) -> Float16 { 41 | Float16(.asin(Float(x))) 42 | } 43 | 44 | @_transparent 45 | public static func atan(_ x: Float16) -> Float16 { 46 | Float16(.atan(Float(x))) 47 | } 48 | 49 | @_transparent 50 | public static func cosh(_ x: Float16) -> Float16 { 51 | Float16(.cosh(Float(x))) 52 | } 53 | 54 | @_transparent 55 | public static func sinh(_ x: Float16) -> Float16 { 56 | Float16(.sinh(Float(x))) 57 | } 58 | 59 | @_transparent 60 | public static func tanh(_ x: Float16) -> Float16 { 61 | Float16(.tanh(Float(x))) 62 | } 63 | 64 | @_transparent 65 | public static func acosh(_ x: Float16) -> Float16 { 66 | Float16(.acosh(Float(x))) 67 | } 68 | 69 | @_transparent 70 | public static func asinh(_ x: Float16) -> Float16 { 71 | Float16(.asinh(Float(x))) 72 | } 73 | 74 | @_transparent 75 | public static func atanh(_ x: Float16) -> Float16 { 76 | Float16(.atanh(Float(x))) 77 | } 78 | 79 | @_transparent 80 | public static func exp(_ x: Float16) -> Float16 { 81 | Float16(.exp(Float(x))) 82 | } 83 | 84 | @_transparent 85 | public static func expMinusOne(_ x: Float16) -> Float16 { 86 | Float16(.expMinusOne(Float(x))) 87 | } 88 | 89 | @_transparent 90 | public static func log(_ x: Float16) -> Float16 { 91 | Float16(.log(Float(x))) 92 | } 93 | 94 | @_transparent 95 | public static func log(onePlus x: Float16) -> Float16 { 96 | Float16(.log(onePlus: Float(x))) 97 | } 98 | 99 | @_transparent 100 | public static func erf(_ x: Float16) -> Float16 { 101 | Float16(.erf(Float(x))) 102 | } 103 | 104 | @_transparent 105 | public static func erfc(_ x: Float16) -> Float16 { 106 | Float16(.erfc(Float(x))) 107 | } 108 | 109 | @_transparent 110 | public static func exp2(_ x: Float16) -> Float16 { 111 | Float16(.exp2(Float(x))) 112 | } 113 | 114 | @_transparent 115 | public static func exp10(_ x: Float16) -> Float16 { 116 | Float16(.exp10(Float(x))) 117 | } 118 | 119 | @_transparent 120 | public static func hypot(_ x: Float16, _ y: Float16) -> Float16 { 121 | if x.isInfinite || y.isInfinite { return .infinity } 122 | let xf = Float(x) 123 | let yf = Float(y) 124 | return Float16(.sqrt(xf*xf + yf*yf)) 125 | } 126 | 127 | @_transparent 128 | public static func gamma(_ x: Float16) -> Float16 { 129 | Float16(.gamma(Float(x))) 130 | } 131 | 132 | @_transparent 133 | public static func log2(_ x: Float16) -> Float16 { 134 | Float16(.log2(Float(x))) 135 | } 136 | 137 | @_transparent 138 | public static func log10(_ x: Float16) -> Float16 { 139 | Float16(.log10(Float(x))) 140 | } 141 | 142 | @_transparent 143 | public static func pow(_ x: Float16, _ y: Float16) -> Float16 { 144 | Float16(.pow(Float(x), Float(y))) 145 | } 146 | 147 | @_transparent 148 | public static func pow(_ x: Float16, _ n: Int) -> Float16 { 149 | // Float16 is simpler than Float or Double, because the range of 150 | // "interesting" exponents is pretty small; anything outside of 151 | // -22707 ... 34061 simply overflows or underflows for every 152 | // x that isn't zero or one. This whole range is representable 153 | // as Float, so we can just use powf as long as we're a little 154 | // bit (get it?) careful to preserve parity. 155 | let clamped = min(max(n, -0x10000), 0x10000) | (n & 1) 156 | return Float16(libm_powf(Float(x), Float(clamped))) 157 | } 158 | 159 | @_transparent 160 | public static func root(_ x: Float16, _ n: Int) -> Float16 { 161 | Float16(.root(Float(x), n)) 162 | } 163 | 164 | @_transparent 165 | public static func atan2(y: Float16, x: Float16) -> Float16 { 166 | Float16(.atan2(y: Float(y), x: Float(x))) 167 | } 168 | 169 | #if !os(Windows) 170 | @_transparent 171 | public static func logGamma(_ x: Float16) -> Float16 { 172 | Float16(.logGamma(Float(x))) 173 | } 174 | #endif 175 | 176 | #if !arch(wasm32) 177 | // WASM doesn't have _Float16 on the C side, so we can't define the C hooks 178 | // that these use. TODO: implement these as Swift builtins instead. 179 | 180 | @_transparent 181 | public static func _relaxedAdd(_ a: Float16, _ b: Float16) -> Float16 { 182 | _numerics_relaxed_addf16(a, b) 183 | } 184 | 185 | @_transparent 186 | public static func _relaxedMul(_ a: Float16, _ b: Float16) -> Float16 { 187 | _numerics_relaxed_mulf16(a, b) 188 | } 189 | #endif 190 | } 191 | 192 | #endif 193 | -------------------------------------------------------------------------------- /Sources/ComplexModule/README.md: -------------------------------------------------------------------------------- 1 | # Complex Numbers 2 | 3 | This module provides a `Complex` number type generic over an underlying `RealType`: 4 | ```swift 5 | import Numerics 6 | let z = Complex(1,1) // z = 1 + i 7 | ``` 8 | This module provides approximate feature parity and memory layout compatibility with C, Fortran, and C++ complex types (although the importer does not map the types for you, buffers may be reinterpreted to shim API defined in other languages). 9 | 10 | The usual arithmetic operators are provided for complex numbers, as well as conversion to and from polar coordinates and many useful properties, plus conformances to the obvious usual protocols: `Equatable`, `Hashable`, `Codable` (if the underlying `RealType` is), and `AlgebraicField` (hence also `AdditiveArithmetic` and `SignedNumeric`). 11 | 12 | The `Complex` type conforms to `ElementaryFunctions`, which makes common transcendental functions like `log`, `pow`, and `sin` available (consult the documentation for `ElementaryFunctions` in the `RealModule` for a full list and more information. 13 | 14 | ### Dependencies: 15 | - `RealModule`. 16 | 17 | ## Design notes 18 | 19 | ### Mixed real-complex arithmetic. 20 | It is tempting to define real-complex arithmetic operators, because we use them as shorthand all the time in mathematics: `z + x` or `2w`. 21 | They are not provided by the Complex module for two reasons: 22 | - Swift generally avoids heterogenous arithmetic operators 23 | - They lead to counter-intuitive behavior of type inference. 24 | For a concrete example of the second point, suppose that heterogeneous arithmetic operators existed, and consider the following snippet: 25 | ```swift 26 | let a: RealType = 1 27 | let b = 2*a 28 | ``` 29 | what is the type of `b`? 30 | 31 | If there is no type context, `b` is ambiguous; `2*a` could be interpreted as `Complex(2)*a` or as `RealType(2)*a`. 32 | That's annoying on its own. However, suppose that we're in a `Complex` type context: 33 | ```swift 34 | extension Complex { 35 | static func doSomething() { 36 | let a: RealType = 1 37 | let b = 2*a // type is inferred as Complex 🤪 38 | } 39 | } 40 | ``` 41 | This makes heterogeneous arithmetic operators a non-option in the short term. 42 | 43 | ### Infinity and nan 44 | C and C++ attempt to define semantics that interpret the signs of infinity and zero. 45 | This is occasionally useful, but it also results in a lot of extra work. 46 | The Swift Numerics `Complex` type does not assign any semantic meaning to the sign of zero and infinity; `(±0,±0)`, are all considered to be encodings of the value zero. 47 | Similarly, `(±inf, y)`, `(x, ±inf)`, `(nan, y)` and `(x, nan)` are all considered to be encodings of a single exceptional value with infinite magnitude and undefined phase. 48 | 49 | Because the phase is undefined, the `real` and `imaginary` properties return `.nan` for non-finite values. 50 | This decision might be revisited once users gain some experience working with the type to make sure that it's a tradeoff that we're happy with, but early experiments show that it greatly simplifies the implementation of some operations without significant tradeoffs in usability. 51 | 52 | ### The magnitude property 53 | The `Numeric` protocol requires a `.magnitude` property, but (deliberately) does not fully specify the semantics. 54 | The most obvious choice for `Complex` would be to use the Euclidean norm (aka the "2-norm", given by `sqrt(real*real + imaginary*imaginary)`). 55 | However, in practice there are good reasons to use something else instead: 56 | 57 | - The 2-norm requires special care to avoid spurious overflow/underflow, but the naive expressions for the 1-norm ("taxicab norm") or ∞-norm ("sup norm") are always correct. 58 | - Even when care is used, near the overflow boundary the 2-norm and the 1-norm are not representable. 59 | As an example, consider `z = Complex(big, big)`, where `big` is `Double.greatestFiniteMagnitude`. 60 | The 1-norm and 2-norm of `z` both overflow (the 1-norm would be `2*big`, and the 2-norm would be `sqrt(2)*big`, neither of which are representable as `Double`), but the ∞-norm is always equal to either `real` or `imaginary`, so it is guaranteed to be representable. 61 | Because of this, the ∞-norm is the obvious alternative; it gives the nicest API surface. 62 | - If we consider the magnitude of more exotic types, like operators, the 1-norm and ∞-norm are significantly easier to compute than the 2-norm (O(n) vs. "no closed form expression, but O(n^3) iterative methods"), so it is nice to establish a precedent of `.magnitude` binding one of these cheaper-to-compute norms. 63 | - The ∞-norm is heavily used in other computational libraries; for example, it is used by the `izamax` and `icamax` functions in BLAS. 64 | 65 | The 2-norm still needs to be available, of course, because sometimes you need it. 66 | This functionality is accessed via the `.length` and `.lengthSquared` properties. 67 | 68 | ### Accuracy of division and multiplication 69 | This library attempts to provide robust division and multiplication operations, with small relative error in a complex norm. It is a non-goal to deliver small componentwise errors. 70 | See `testBaudinSmith` in `ComplexTests.swift` for examples of cases where we have tiny relative error but large error in one of the result components considered in isolation. 71 | This is the right tradeoff for a general-purpose library because it allows us to use the naive formulas for multiplication and division (which are fast), and do a simple check to see if we need to re-do the computation more carefully to avoid spurious overflow or underflow. 72 | It's also about as accurate as multiplication. 73 | 74 | Implementing the method of Baudin and Smith (or any other method that delivers similarly small componentwise error) would unduly slow down the common case for relatively little benefit--componentwise error bounds are rarely necessary when working over the complex numbers. 75 | 76 | That said, a PR that implements multiplication and division *functions* with tight componentwise error bounds would be a welcome addition to the library. 77 | -------------------------------------------------------------------------------- /Sources/RealModule/AlgebraicField.swift: -------------------------------------------------------------------------------- 1 | //===--- AlgebraicField.swift ---------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | /// A type modeling an algebraic [field]. Refines the `SignedNumeric` protocol, 13 | /// adding division. 14 | /// 15 | /// A field is a set on which addition, subtraction, multiplication, and 16 | /// division are defined, and behave basically like those operations on 17 | /// the real numbers. More precisely, a field is a commutative group under 18 | /// its addition, the non-zero elements of the field form a commutative 19 | /// group under its multiplication, and the distributive law holds. 20 | /// 21 | /// Some common examples of fields include: 22 | /// 23 | /// - the rational numbers 24 | /// - the real numbers 25 | /// - the complex numbers 26 | /// - the integers modulo a prime 27 | /// 28 | /// The most familiar example of a thing that is *not* a field is the integers. 29 | /// This may be surprising, since integers seem to have addition, subtraction, 30 | /// multiplication and division. Why don't they form a field? 31 | /// 32 | /// Because integer multiplication does not form a group; it's commutative and 33 | /// associative, but integers do not have multiplicative inverses. 34 | /// I.e. if a is any integer other than 1 or -1, there is no integer b such 35 | /// that `a*b = 1`. The existence of inverses is requried to form a field. 36 | /// 37 | /// If a type `T` conforms to the ``Real`` protocol, then `T` and `Complex` 38 | /// both conform to `AlgebraicField`. 39 | /// 40 | /// See also Swift's `SignedNumeric`, `Numeric` and `AdditiveArithmetic` 41 | /// protocols. 42 | /// 43 | /// [field]: https://en.wikipedia.org/wiki/Field_(mathematics) 44 | public protocol AlgebraicField: SignedNumeric where Magnitude: AlgebraicField { 45 | 46 | /// Replaces a with the (approximate) quotient `a/b`. 47 | static func /=(a: inout Self, b: Self) 48 | 49 | /// The (approximate) quotient `a/b`. 50 | static func /(a: Self, b: Self) -> Self 51 | 52 | /// The (approximate) reciprocal (multiplicative inverse) of this number, 53 | /// if it is representable. 54 | /// 55 | /// If reciprocal is non-nil, you can replace division by self with 56 | /// multiplication by reciprocal and either get exact the same result 57 | /// (for finite fields) or approximately the same result up to a typical 58 | /// rounding error (for floating-point formats). 59 | /// 60 | /// If self is zero and the type has no representation for infinity (as 61 | /// in a typical finite field implementation), or if a reciprocal would 62 | /// overflow or underflow such that it cannot be accurately represented, 63 | /// the result is nil. 64 | /// 65 | /// Note that `.zero.reciprocal`, somewhat surprisingly, is *not* nil 66 | /// for `Real` or `Complex` types, because these types have an 67 | /// `.infinity` value that acts as the reciprocal of `.zero`. 68 | /// 69 | /// If `b.reciprocal` is non-nil, you may be able to replace division by `b` 70 | /// with multiplication by this value. It is not advantageous to do this 71 | /// for an isolated division unless it is a compile-time constant visible 72 | /// to the compiler, but if you are dividing many values by a single 73 | /// denominator, this will often be a significant performance win. 74 | /// 75 | /// Note that this will slightly perturb results for fields with approximate 76 | /// arithmetic, such as real or complex types--using a normal division 77 | /// is generally more accurate--but no catastrophic loss of accuracy will 78 | /// result. For fields with exact arithmetic, the results are necessarily 79 | /// identical. 80 | /// 81 | /// A typical use case looks something like this: 82 | /// ``` 83 | /// func divide(data: [T], by divisor: T) -> [T] { 84 | /// // If divisor is well-scaled, multiply by reciprocal. 85 | /// if let recip = divisor.reciprocal { 86 | /// return data.map { $0 * recip } 87 | /// } 88 | /// // Fallback on using division. 89 | /// return data.map { $0 / divisor } 90 | /// } 91 | /// ``` 92 | var reciprocal: Self? { get } 93 | 94 | /// `a + b`, with the optimizer licensed to reassociate and form FMAs. 95 | static func _relaxedAdd(_ a: Self, _ b: Self) -> Self 96 | 97 | /// `a * b`, with the optimizer licensed to reassociate and form FMAs. 98 | static func _relaxedMul(_ a: Self, _ b: Self) -> Self 99 | } 100 | 101 | extension AlgebraicField { 102 | @_transparent 103 | public static func /(a: Self, b: Self) -> Self { 104 | var result = a 105 | result /= b 106 | return result 107 | } 108 | 109 | // Implementations should be *conservative* with the reciprocal property; 110 | // it is OK to return `nil` even in cases where a reciprocal could be 111 | // represented. For this reason, a default implementation that simply 112 | // always returns `nil` is correct, but conforming types should provide 113 | // a better implementation if possible. 114 | @_transparent 115 | public var reciprocal: Self? { 116 | return nil 117 | } 118 | 119 | // It's always OK to simply fall back on normal arithmetic, and for any 120 | // field with exact arithmetic, this is the correct definition. 121 | @_transparent 122 | public static func _relaxedAdd(_ a: Self, _ b: Self) -> Self { 123 | a + b 124 | } 125 | 126 | // It's always OK to simply fall back on normal arithmetic, and for any 127 | // field with exact arithmetic, this is the correct definition. 128 | @_transparent 129 | public static func _relaxedMul(_ a: Self, _ b: Self) -> Self { 130 | a * b 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /Sources/RealModule/Float+Real.swift: -------------------------------------------------------------------------------- 1 | //===--- Float+Real.swift -------------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | import _NumericsShims 13 | 14 | extension Float: Real { 15 | @_transparent 16 | public static func cos(_ x: Float) -> Float { 17 | libm_cosf(x) 18 | } 19 | 20 | @_transparent 21 | public static func sin(_ x: Float) -> Float { 22 | libm_sinf(x) 23 | } 24 | 25 | @_transparent 26 | public static func tan(_ x: Float) -> Float { 27 | libm_tanf(x) 28 | } 29 | 30 | @_transparent 31 | public static func acos(_ x: Float) -> Float { 32 | libm_acosf(x) 33 | } 34 | 35 | @_transparent 36 | public static func asin(_ x: Float) -> Float { 37 | libm_asinf(x) 38 | } 39 | 40 | @_transparent 41 | public static func atan(_ x: Float) -> Float { 42 | libm_atanf(x) 43 | } 44 | 45 | @_transparent 46 | public static func cosh(_ x: Float) -> Float { 47 | libm_coshf(x) 48 | } 49 | 50 | @_transparent 51 | public static func sinh(_ x: Float) -> Float { 52 | libm_sinhf(x) 53 | } 54 | 55 | @_transparent 56 | public static func tanh(_ x: Float) -> Float { 57 | libm_tanhf(x) 58 | } 59 | 60 | @_transparent 61 | public static func acosh(_ x: Float) -> Float { 62 | libm_acoshf(x) 63 | } 64 | 65 | @_transparent 66 | public static func asinh(_ x: Float) -> Float { 67 | libm_asinhf(x) 68 | } 69 | 70 | @_transparent 71 | public static func atanh(_ x: Float) -> Float { 72 | libm_atanhf(x) 73 | } 74 | 75 | @_transparent 76 | public static func exp(_ x: Float) -> Float { 77 | libm_expf(x) 78 | } 79 | 80 | @_transparent 81 | public static func expMinusOne(_ x: Float) -> Float { 82 | libm_expm1f(x) 83 | } 84 | 85 | @_transparent 86 | public static func log(_ x: Float) -> Float { 87 | libm_logf(x) 88 | } 89 | 90 | @_transparent 91 | public static func log(onePlus x: Float) -> Float { 92 | libm_log1pf(x) 93 | } 94 | 95 | @_transparent 96 | public static func erf(_ x: Float) -> Float { 97 | libm_erff(x) 98 | } 99 | 100 | @_transparent 101 | public static func erfc(_ x: Float) -> Float { 102 | libm_erfcf(x) 103 | } 104 | 105 | @_transparent 106 | public static func exp2(_ x: Float) -> Float { 107 | libm_exp2f(x) 108 | } 109 | 110 | #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) 111 | @_transparent 112 | public static func exp10(_ x: Float) -> Float { 113 | libm_exp10f(x) 114 | } 115 | #endif 116 | 117 | @_transparent 118 | public static func hypot(_ x: Float, _ y: Float) -> Float { 119 | libm_hypotf(x, y) 120 | } 121 | 122 | @_transparent 123 | public static func gamma(_ x: Float) -> Float { 124 | libm_tgammaf(x) 125 | } 126 | 127 | @_transparent 128 | public static func log2(_ x: Float) -> Float { 129 | libm_log2f(x) 130 | } 131 | 132 | @_transparent 133 | public static func log10(_ x: Float) -> Float { 134 | libm_log10f(x) 135 | } 136 | 137 | @_transparent 138 | public static func pow(_ x: Float, _ y: Float) -> Float { 139 | guard x >= 0 else { return .nan } 140 | if x == 0 && y == 0 { return .nan } 141 | return libm_powf(x, y) 142 | } 143 | 144 | @_transparent 145 | public static func pow(_ x: Float, _ n: Int) -> Float { 146 | // If n is exactly representable as Float, we can just call powf: 147 | if let y = Float(exactly: n) { 148 | return libm_powf(x, y) 149 | } 150 | // Otherwise, n is too large to losslessly represent as Float. 151 | // The range of "interesting" n is -1488522191 ... 1744361944; outside 152 | // of this range, all x != 1 overflow or underflow, so only the parity 153 | // of x matters. We don't really care about the specific range at all, 154 | // only that the bounds fit exactly into two Floats. 155 | // 156 | // We do, however, need to be careful that high and low both have the 157 | // same sign as n (consult the Double implementation for details of why 158 | // this matters), so we need to be a little bit careful constructing 159 | // them. 160 | // 161 | // Unlike the Double implementation, when n is very large, high will 162 | // get rounded here; that's OK because it does not change the sign or 163 | // parity, which are the only two bits that matter for such large 164 | // exponents in Float. 165 | let mask = Int(truncatingIfNeeded: 0xffffff) 166 | let round = n < 0 ? mask : 0 167 | let high = (n &+ round) & ~mask 168 | let low = n &- high 169 | return libm_powf(x, Float(low)) * libm_powf(x, Float(high)) 170 | } 171 | 172 | @_transparent 173 | public static func root(_ x: Float, _ n: Int) -> Float { 174 | guard x >= 0 || n % 2 != 0 else { return .nan } 175 | // Workaround the issue mentioned below for the specific case of n = 3 176 | // where we can fallback on cbrt. 177 | if n == 3 { return libm_cbrtf(x) } 178 | // TODO: this implementation is not quite correct, because either n or 179 | // 1/n may be not be representable as Float. 180 | return Float(signOf: x, magnitudeOf: libm_powf(x.magnitude, 1/Float(n))) 181 | } 182 | 183 | @_transparent 184 | public static func atan2(y: Float, x: Float) -> Float { 185 | libm_atan2f(y, x) 186 | } 187 | 188 | #if !os(Windows) 189 | @_transparent 190 | public static func logGamma(_ x: Float) -> Float { 191 | var dontCare: Int32 = 0 192 | return libm_lgammaf(x, &dontCare) 193 | } 194 | #endif 195 | 196 | @_transparent 197 | public static func _relaxedAdd(_ a: Float, _ b: Float) -> Float { 198 | _numerics_relaxed_addf(a, b) 199 | } 200 | 201 | @_transparent 202 | public static func _relaxedMul(_ a: Float, _ b: Float) -> Float { 203 | _numerics_relaxed_mulf(a, b) 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /Sources/RealModule/AugmentedArithmetic.swift: -------------------------------------------------------------------------------- 1 | //===--- AugmentedArithmetic.swift ----------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2020-2025 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | public enum Augmented { } 13 | 14 | extension Augmented { 15 | /// The product `a * b` represented as an implicit sum `head + tail`. 16 | /// 17 | /// `head` is the correctly rounded value of `a*b`. If no overflow or 18 | /// underflow occurs, `tail` represents the rounding error incurred in 19 | /// computing `head`, such that the exact product is the sum of `head` 20 | /// and `tail` computed without rounding. 21 | /// 22 | /// This operation is sometimes called "twoProd" or "twoProduct". 23 | /// 24 | /// Edge Cases: 25 | /// 26 | /// - `head` is always the IEEE 754 product `a * b`. 27 | /// - If `head` is not finite, `tail` is unspecified and should not be 28 | /// interpreted as having any meaning (it may be `NaN` or `infinity`). 29 | /// - When `head` is close to the underflow boundary, the rounding error 30 | /// may not be representable due to underflow, and `tail` will be rounded. 31 | /// If `head` is very small, `tail` may even be zero, even though the 32 | /// product is not exact. 33 | /// - If `head` is zero, `tail` is also a zero with unspecified sign. 34 | /// 35 | /// Postconditions: 36 | /// 37 | /// - If `head` is normal, then `abs(tail) < head.ulp`. 38 | /// Assuming IEEE 754 default rounding, `abs(tail) <= head.ulp/2`. 39 | /// - If both `head` and `tail` are normal, then `a * b` is exactly 40 | /// equal to `head + tail` when computed as real numbers. 41 | @_transparent 42 | public static func product( 43 | _ a: T, _ b: T 44 | ) -> (head: T, tail: T) { 45 | let head = a*b 46 | // TODO: consider providing an FMA-less implementation for use when 47 | // targeting platforms without hardware FMA support. This works everywhere, 48 | // falling back on the C math.h fma funcions, but may be slow on older x86. 49 | let tail = (-head).addingProduct(a, b) 50 | return (head, tail) 51 | } 52 | 53 | /// The sum `a + b` represented as an implicit sum `head + tail`. 54 | /// 55 | /// - Parameters: 56 | /// - a: The summand with larger magnitude. 57 | /// - b: The summand with smaller magnitude. 58 | /// 59 | /// `head` is the correctly rounded value of `a + b`. `tail` is the 60 | /// error from that computation rounded to the closest representable 61 | /// value. 62 | /// 63 | /// > Note: 64 | /// > `tail` is guaranteed to be the best approximation to the error of 65 | /// the sum only if `large.magnitude` >= `small.magnitude`. If this is 66 | /// not the case, then `head` is the correctly rounded sum, but `tail` 67 | /// is not guaranteed to be the exact error. If you do not know a priori 68 | /// how the magnitudes of `a` and `b` compare, you likely want to use 69 | /// ``sum(_:_:)`` instead. 70 | /// 71 | /// Unlike ``product(_:_:)``, the rounding error of `sum` never underflows. 72 | /// 73 | /// This operation is sometimes called ["fastTwoSum"]. 74 | /// 75 | /// > Note: 76 | /// > Classical fastTwoSum does not work when `radix` is 10. This function 77 | /// will fall back on another algorithm for decimal floating-point types 78 | /// to ensure correct results. 79 | /// 80 | /// Edge Cases: 81 | /// 82 | /// - `head` is always the IEEE 754 sum `a + b`. 83 | /// - If `head` is not finite, `tail` is unspecified and should not be 84 | /// interpreted as having any meaning (it may be `NaN` or `infinity`). 85 | /// 86 | /// Postconditions: 87 | /// 88 | /// - If `head` is normal, then `abs(tail) < head.ulp`. 89 | /// Assuming IEEE 754 default rounding, `abs(tail) <= head.ulp/2`. 90 | /// 91 | /// ["fastTwoSum"]: https://en.wikipedia.org/wiki/2Sum 92 | @_transparent 93 | public static func sum( 94 | large a: T, small b: T 95 | ) -> (head: T, tail: T) { 96 | // Fall back on 2Sum if radix != 2. Future implementations might use an 97 | // cheaper algorithm specialized for decimal FP, but must deliver a 98 | // correct result if the preconditions are satisfied. 99 | guard T.radix == 2 else { return sum(a, b) } 100 | // Fast2Sum: 101 | let head = a + b 102 | let tail = a - head + b 103 | return (head, tail) 104 | } 105 | 106 | /// The sum `a + b` represented as an implicit sum `head + tail`. 107 | /// 108 | /// `head` is the correctly rounded value of `a + b`. `tail` is the 109 | /// error from that computation rounded to the closest representable 110 | /// value. 111 | /// 112 | /// The magnitude of the summands does not change the result of this 113 | /// operation. If you know statically that `a.magnitude >= b.magnitude`, 114 | /// you may want to consider using ``sum(large:small:)`` to get the same 115 | /// result somewhat more efficiently. If you do not have such a static 116 | /// bound, you usually want to use this function instead. 117 | /// 118 | /// Unlike ``product(_:_:)``, the rounding error of `sum` never underflows. 119 | /// 120 | /// This operation is sometimes called ["twoSum"]. 121 | /// 122 | /// - Parameters: 123 | /// - a: One of the summands 124 | /// - b: The other summand 125 | /// 126 | /// Edge Cases: 127 | /// 128 | /// - `head` is always the IEEE 754 sum `a + b`. 129 | /// - If `head` is not finite, `tail` is unspecified and should not be 130 | /// interpreted as having any meaning (it may be `NaN` or `infinity`). 131 | /// 132 | /// Postconditions: 133 | /// 134 | /// - If `head` is normal, then `abs(tail) < head.ulp`. 135 | /// Assuming IEEE 754 default rounding, `abs(tail) <= head.ulp/2`. 136 | /// 137 | /// ["twoSum"]: https://en.wikipedia.org/wiki/2Sum 138 | @_transparent 139 | public static func sum( 140 | _ a: T, _ b: T 141 | ) -> (head: T, tail: T) { 142 | let head = a + b 143 | let x = head - b 144 | let y = head - x 145 | let tail = (a - x) + (b - y) 146 | return (head, tail) 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /Tests/RealTests/ApproximateEqualityTests.swift: -------------------------------------------------------------------------------- 1 | //===--- ApproximateEqualityTests.swift -----------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2020 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import RealModule 14 | import XCTest 15 | import _TestSupport 16 | 17 | final class ApproximateEqualityTests: XCTestCase { 18 | 19 | func testSpecials(absolute tol: T) { 20 | let zero = T.zero 21 | let gfm = T.greatestFiniteMagnitude 22 | let inf = T.infinity 23 | let nan = T.nan 24 | XCTAssertTrue(zero.isApproximatelyEqual(to: zero, absoluteTolerance: tol)) 25 | XCTAssertTrue(zero.isApproximatelyEqual(to:-zero, absoluteTolerance: tol)) 26 | XCTAssertFalse(inf.isApproximatelyEqual(to: gfm, absoluteTolerance: tol)) 27 | XCTAssertFalse(gfm.isApproximatelyEqual(to: inf, absoluteTolerance: tol)) 28 | XCTAssertTrue(inf.isApproximatelyEqual(to: inf, absoluteTolerance: tol)) 29 | XCTAssertTrue(inf.isApproximatelyEqual(to: inf, absoluteTolerance: tol)) 30 | XCTAssertFalse(nan.isApproximatelyEqual(to: nan, absoluteTolerance: tol)) 31 | } 32 | 33 | func testSpecials(relative tol: T) { 34 | let zero = T.zero 35 | let gfm = T.greatestFiniteMagnitude 36 | let inf = T.infinity 37 | let nan = T.nan 38 | XCTAssertTrue(zero.isApproximatelyEqual(to: zero, relativeTolerance: tol)) 39 | XCTAssertTrue(zero.isApproximatelyEqual(to:-zero, relativeTolerance: tol)) 40 | XCTAssertFalse(inf.isApproximatelyEqual(to: gfm, relativeTolerance: tol)) 41 | XCTAssertFalse(gfm.isApproximatelyEqual(to: inf, relativeTolerance: tol)) 42 | XCTAssertTrue(inf.isApproximatelyEqual(to: inf, relativeTolerance: tol)) 43 | XCTAssertTrue(inf.isApproximatelyEqual(to: inf, relativeTolerance: tol)) 44 | XCTAssertFalse(nan.isApproximatelyEqual(to: nan, relativeTolerance: tol)) 45 | } 46 | 47 | func testSpecials(_ type: T.Type) { 48 | XCTAssertTrue(T.zero.isApproximatelyEqual(to: .zero)) 49 | XCTAssertTrue(T.zero.isApproximatelyEqual(to:-.zero)) 50 | testSpecials(absolute: T.zero) 51 | testSpecials(absolute: T.leastNormalMagnitude) 52 | testSpecials(absolute: T.greatestFiniteMagnitude) 53 | testSpecials(relative: T.zero) 54 | testSpecials(relative: T.ulpOfOne) 55 | testSpecials(relative: T(1).nextDown) 56 | testSpecials(relative: T(1)) 57 | } 58 | 59 | func testDefaults(_ type: T.Type) { 60 | let e = T.ulpOfOne.squareRoot() 61 | XCTAssertTrue(T(1).isApproximatelyEqual(to: 1 + e)) 62 | XCTAssertTrue(T(1).isApproximatelyEqual(to: 1 - e/2)) 63 | XCTAssertFalse(T(1).isApproximatelyEqual(to: 1 + 2*e)) 64 | XCTAssertFalse(T(1).isApproximatelyEqual(to: 1 - 3*e/2)) 65 | } 66 | 67 | func testRandom(_ type: T.Type) where T: FixedWidthFloatingPoint & Real { 68 | var g = SystemRandomNumberGenerator() 69 | // Generate a bunch of random values in a small interval and a tolerance 70 | // and use them to check that various properties that we would like to 71 | // hold actually do. 72 | var x = [1] + (0 ..< 64).map { 73 | _ in T.random(in: 1 ..< 2, using: &g) 74 | } + [2] 75 | x.sort() 76 | // We have 66 values in 1 ... 2, so if we use a tolerance of around 1/64, 77 | // at least some of the pairs will compare equal with tolerance. 78 | let tol = T.random(in: 1/64 ... 1/32, using: &g) 79 | // We're going to walk the values in order, validating that some common- 80 | // sense properties hold. 81 | for i in x.indices { 82 | // reflexivity 83 | XCTAssertTrue(x[i].isApproximatelyEqual(to: x[i])) 84 | XCTAssertTrue(x[i].isApproximatelyEqual(to: x[i], relativeTolerance: tol)) 85 | XCTAssertTrue(x[i].isApproximatelyEqual(to: x[i], absoluteTolerance: tol)) 86 | for j in i ..< x.endIndex { 87 | // commutativity 88 | XCTAssertTrue( 89 | x[i].isApproximatelyEqual(to: x[j], relativeTolerance: tol) == 90 | x[j].isApproximatelyEqual(to: x[i], relativeTolerance: tol) 91 | ) 92 | XCTAssertTrue( 93 | x[i].isApproximatelyEqual(to: x[j], absoluteTolerance: tol) == 94 | x[j].isApproximatelyEqual(to: x[i], absoluteTolerance: tol) 95 | ) 96 | // scale invariance for relative comparisons 97 | let scale = T( 98 | sign:.plus, 99 | exponent: T.Exponent.random(in: T.leastNormalMagnitude.exponent ..< T.greatestFiniteMagnitude.exponent), 100 | significand: 1 101 | ) 102 | XCTAssertTrue( 103 | x[i].isApproximatelyEqual(to: x[j], relativeTolerance: tol) == 104 | (scale*x[i]).isApproximatelyEqual(to: scale*x[j], relativeTolerance: tol) 105 | ) 106 | } 107 | // if a ≤ b ≤ c, and a ≈ c, then a ≈ b and b ≈ c (relative tolerance) 108 | var left = x.firstIndex { x[i].isApproximatelyEqual(to: $0, relativeTolerance: tol) } 109 | var right = x.lastIndex { x[i].isApproximatelyEqual(to: $0, relativeTolerance: tol) } 110 | if let l = left, let r = right { 111 | for j in l ..< r { 112 | XCTAssertTrue(x[i].isApproximatelyEqual(to: x[j], relativeTolerance: tol)) 113 | } 114 | } 115 | // if a ≤ b ≤ c, and a ≈ c, then a ≈ b and b ≈ c (absolute tolerance) 116 | left = x.firstIndex { x[i].isApproximatelyEqual(to: $0, absoluteTolerance: tol) } 117 | right = x.lastIndex { x[i].isApproximatelyEqual(to: $0, absoluteTolerance: tol) } 118 | if let l = left, let r = right { 119 | for j in l ..< r { 120 | XCTAssertTrue(x[i].isApproximatelyEqual(to: x[j], absoluteTolerance: tol)) 121 | } 122 | } 123 | } 124 | } 125 | 126 | func testFloat() { 127 | testSpecials(Float.self) 128 | testDefaults(Float.self) 129 | testRandom(Float.self) 130 | } 131 | 132 | func testDouble() { 133 | testSpecials(Double.self) 134 | testDefaults(Double.self) 135 | testRandom(Double.self) 136 | } 137 | 138 | #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android) 139 | func testFloat80() { 140 | testSpecials(Float80.self) 141 | testDefaults(Float80.self) 142 | testRandom(Float80.self) 143 | } 144 | #endif 145 | } 146 | -------------------------------------------------------------------------------- /Sources/RealModule/Real.swift: -------------------------------------------------------------------------------- 1 | //===--- Real.swift -------------------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | /// A type that models the real numbers. 13 | /// 14 | /// Types conforming to this protocol provide the arithmetic and utility 15 | /// operations defined by the `FloatingPoint` protocol, and provide all of the 16 | /// math functions defined by the `ElementaryFunctions` and `RealFunctions` 17 | /// protocols. This protocol does not add any additional conformances itself, 18 | /// but is very useful as a protocol against which to write generic code. For 19 | /// example, we can naturally write a generic implementation of a sigmoid 20 | /// function: 21 | /// ``` 22 | /// func sigmoid(_ x: T) -> T { 23 | /// return 1/(1 + .exp(-x)) 24 | /// } 25 | /// ``` 26 | /// See also `ElementaryFunctions`, `RealFunctions` and `AlgebraicField`. 27 | public protocol Real: FloatingPoint, RealFunctions, AlgebraicField { } 28 | 29 | // While `Real` does not provide any additional customization points, 30 | // it does allow us to default the implementation of a few operations, 31 | // and also provides `signGamma`. 32 | extension Real { 33 | // Most math libraries do not provide exp10, so we need a default 34 | // implementation. This is not a great one (if the underlying math 35 | // library does not have a sub-ulp accurate pow, this will not get 36 | // exact powers of ten right), but suffices in the short term. 37 | @_transparent 38 | public static func exp10(_ x: Self) -> Self { 39 | pow(10, x) 40 | } 41 | 42 | /// cos(x) - 1, computed in such a way as to maintain accuracy for small x. 43 | /// 44 | /// See also ``ElementaryFunctions/expMinusOne(_:)``. 45 | @_transparent 46 | public static func cosMinusOne(_ x: Self) -> Self { 47 | let sinxOver2 = sin(x/2) 48 | return -2*sinxOver2*sinxOver2 49 | } 50 | 51 | #if !os(Windows) 52 | public static func signGamma(_ x: Self) -> FloatingPointSign { 53 | // Gamma is strictly positive for x >= 0. 54 | if x >= 0 { return .plus } 55 | // For negative x, we arbitrarily choose to assign a sign of .plus to the 56 | // poles. 57 | let trunc = x.rounded(.towardZero) 58 | if x == trunc { return .plus } 59 | // Otherwise, signGamma is .minus if the integral part of x is even. 60 | return trunc.isEven ? .minus : .plus 61 | } 62 | 63 | // Determines if this value is even, assuming that it is an integer. 64 | @inline(__always) 65 | private var isEven: Bool { 66 | if Self.radix == 2 { 67 | // For binary types, we can just check if x/2 is an integer. This works 68 | // because x/2 is always computed exactly. 69 | let half = self/2 70 | return half == half.rounded(.towardZero) 71 | } else { 72 | // For decimal types, it's not quite that simple, because x/2 is not 73 | // necessarily computed exactly. As an example, suppose that we had a 74 | // decimal type with a one digit significand, and self = 7. Then self/2 75 | // would round to 4, and we would (wrongly) conclude that it was an 76 | // integer, and hence that self was even. 77 | // 78 | // Instead, for decimal types, we check if 2*trunc(self/2) == self, 79 | // using an FMA; this is always correct; this approach works for any 80 | // radix, but the previous method is more efficient for radix == 2. 81 | let half = self/2 82 | return self.addingProduct(-2, half.rounded(.towardZero)) == 0 83 | } 84 | } 85 | #endif 86 | 87 | @_transparent 88 | public static func _mulAdd(_ a: Self, _ b: Self, _ c: Self) -> Self { 89 | a*b + c 90 | } 91 | 92 | @_transparent 93 | public static func sqrt(_ x: Self) -> Self { 94 | x.squareRoot() 95 | } 96 | 97 | /// The (approximate) reciprocal (multiplicative inverse) of this number, 98 | /// if it is representable. 99 | /// 100 | /// If `a` if finite and nonzero, and `1/a` overflows or underflows, 101 | /// then `a.reciprocal` is `nil`. Otherwise, `a.reciprocal` is `1/a`. 102 | /// 103 | /// If `b.reciprocal` is non-nil, you may be able to replace division by `b` 104 | /// with multiplication by this value. It is not advantageous to do this 105 | /// for an isolated division unless it is a compile-time constant visible 106 | /// to the compiler, but if you are dividing many values by a single 107 | /// denominator, this will often be a significant performance win. 108 | /// 109 | /// A typical use case looks something like this: 110 | /// ``` 111 | /// func divide(data: [T], by divisor: T) -> [T] { 112 | /// // If divisor is well-scaled, multiply by reciprocal. 113 | /// if let recip = divisor.reciprocal { 114 | /// return data.map { $0 * recip } 115 | /// } 116 | /// // Fallback on using division. 117 | /// return data.map { $0 / divisor } 118 | /// } 119 | /// ``` 120 | /// 121 | /// Error Bounds: 122 | /// 123 | /// Multiplying by the reciprocal instead of dividing will slightly 124 | /// perturb results. For example `5.0 / 3` is 1.6666666666666667, but 125 | /// `5.0 * 3.reciprocal!` is 1.6666666666666665. 126 | /// 127 | /// The error of a normal division is bounded by half an ulp of the 128 | /// result; we can derive a quick error bound for multiplication by 129 | /// the real reciprocal (when it exists) as follows (I will use circle 130 | /// operators to denote real-number arithmetic, and normal operators 131 | /// for floating-point arithmetic): 132 | /// ``` 133 | /// a * b.reciprocal! = a * (1/b) 134 | /// = a * (1 ⊘ b)(1 + δ₁) 135 | /// = (a ⊘ b)(1 + δ₁)(1 + δ₂) 136 | /// = (a ⊘ b)(1 + δ₁ + δ₂ + δ₁δ₂) 137 | /// ``` 138 | /// where `0 < δᵢ <= ulpOfOne/2`. This gives a roughly 1-ulp error, 139 | /// about twice the error bound we get using division. For most 140 | /// purposes this is an acceptable error, but if you need to match 141 | /// results obtained using division, you should not use this. 142 | @inlinable 143 | public var reciprocal: Self? { 144 | let recip = 1/self 145 | if recip.isNormal || isZero || !isFinite { 146 | return recip 147 | } 148 | return nil 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /Tests/IntegerUtilitiesTests/ShiftTests.swift: -------------------------------------------------------------------------------- 1 | //===--- ShiftTests.swift -------------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2021-2024 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import IntegerUtilities 14 | import XCTest 15 | import _TestSupport 16 | 17 | final class IntegerUtilitiesShiftTests: XCTestCase { 18 | 19 | func testRoundingShift( 20 | _ value: T, _ count: C, rounding rule: RoundingRule 21 | ) where T: FixedWidthInteger, C: BinaryInteger { 22 | let floor = value >> count 23 | let lost = value &- floor << count 24 | let exact = count <= 0 || lost == 0 25 | let ceiling = exact ? floor : floor &+ 1 26 | let expected: T 27 | if exact { expected = floor } 28 | else { 29 | switch rule { 30 | case .down: 31 | expected = floor 32 | case .up: 33 | expected = ceiling 34 | case .towardZero: 35 | expected = value < 0 ? ceiling : floor 36 | case .awayFromZero: 37 | expected = value > 0 ? ceiling : floor 38 | case .toOdd: 39 | expected = floor | (exact ? 0 : 1) 40 | case .toNearestOrDown: 41 | let step = value.shifted(rightBy: count - 2, rounding: .toOdd) 42 | switch step & 0b11 { 43 | case 0b01: expected = floor 44 | case 0b10: expected = floor 45 | case 0b11: expected = ceiling 46 | default: preconditionFailure() 47 | } 48 | case .toNearestOrUp: 49 | let step = value.shifted(rightBy: count - 2, rounding: .toOdd) 50 | switch step & 0b11 { 51 | case 0b01: expected = floor 52 | case 0b10: expected = ceiling 53 | case 0b11: expected = ceiling 54 | default: preconditionFailure() 55 | } 56 | case .toNearestOrZero: 57 | let step = value.shifted(rightBy: count - 2, rounding: .toOdd) 58 | switch step & 0b11 { 59 | case 0b01: expected = floor 60 | case 0b10: expected = value > 0 ? floor : ceiling 61 | case 0b11: expected = ceiling 62 | default: preconditionFailure() 63 | } 64 | case .toNearestOrAway: 65 | let step = value.shifted(rightBy: count - 2, rounding: .toOdd) 66 | switch step & 0b11 { 67 | case 0b01: expected = floor 68 | case 0b10: expected = value > 0 ? ceiling : floor 69 | case 0b11: expected = ceiling 70 | default: preconditionFailure() 71 | } 72 | case .toNearestOrEven: 73 | let step = value.shifted(rightBy: count - 2, rounding: .toOdd) 74 | switch step & 0b11 { 75 | case 0b01: expected = floor 76 | case 0b10: expected = floor & 1 == 0 ? floor : ceiling 77 | case 0b11: expected = ceiling 78 | default: preconditionFailure() 79 | } 80 | case .requireExact: 81 | preconditionFailure() 82 | } 83 | let observed = value.shifted(rightBy: count, rounding: rule) 84 | if observed != expected { 85 | print("Error found in \(T.self).shifted(rightBy: \(count), rounding: \(rule)).") 86 | print(" Value: \(String(value, radix: 2))") 87 | print("Expected: \(String(expected, radix: 2))") 88 | print("Observed: \(String(observed, radix: 2))") 89 | XCTFail() 90 | } 91 | } 92 | } 93 | 94 | func testRoundingShift( 95 | _ type: T.Type, rounding rule: RoundingRule 96 | ) { 97 | for count in -2*T.bitWidth ... 2*T.bitWidth { 98 | // zero shifted by anything is always zero 99 | XCTAssertEqual(0, (0 as T).shifted(rightBy: count, rounding: rule)) 100 | for _ in 0 ..< 100 { 101 | testRoundingShift(T.random(in: .min ... .max), count, rounding: rule) 102 | } 103 | } 104 | 105 | for count in Int8.min ... .max { 106 | testRoundingShift(T.random(in: .min ... .max), count, rounding: rule) 107 | } 108 | } 109 | 110 | func testRoundingShifts() { 111 | testRoundingShift(Int8.self, rounding: .down) 112 | testRoundingShift(Int8.self, rounding: .up) 113 | testRoundingShift(Int8.self, rounding: .towardZero) 114 | testRoundingShift(Int8.self, rounding: .awayFromZero) 115 | testRoundingShift(Int8.self, rounding: .toNearestOrUp) 116 | testRoundingShift(Int8.self, rounding: .toNearestOrDown) 117 | testRoundingShift(Int8.self, rounding: .toNearestOrZero) 118 | testRoundingShift(Int8.self, rounding: .toNearestOrAway) 119 | testRoundingShift(Int8.self, rounding: .toNearestOrEven) 120 | testRoundingShift(Int8.self, rounding: .toOdd) 121 | 122 | testRoundingShift(UInt8.self, rounding: .down) 123 | testRoundingShift(UInt8.self, rounding: .up) 124 | testRoundingShift(UInt8.self, rounding: .towardZero) 125 | testRoundingShift(UInt8.self, rounding: .awayFromZero) 126 | testRoundingShift(UInt8.self, rounding: .toNearestOrUp) 127 | testRoundingShift(UInt8.self, rounding: .toNearestOrDown) 128 | testRoundingShift(UInt8.self, rounding: .toNearestOrZero) 129 | testRoundingShift(UInt8.self, rounding: .toNearestOrAway) 130 | testRoundingShift(UInt8.self, rounding: .toNearestOrEven) 131 | testRoundingShift(UInt8.self, rounding: .toOdd) 132 | 133 | testRoundingShift(Int.self, rounding: .down) 134 | testRoundingShift(Int.self, rounding: .up) 135 | testRoundingShift(Int.self, rounding: .towardZero) 136 | testRoundingShift(Int.self, rounding: .awayFromZero) 137 | testRoundingShift(Int.self, rounding: .toNearestOrUp) 138 | testRoundingShift(Int.self, rounding: .toNearestOrDown) 139 | testRoundingShift(Int.self, rounding: .toNearestOrZero) 140 | testRoundingShift(Int.self, rounding: .toNearestOrAway) 141 | testRoundingShift(Int.self, rounding: .toNearestOrEven) 142 | testRoundingShift(Int.self, rounding: .toOdd) 143 | 144 | testRoundingShift(UInt.self, rounding: .down) 145 | testRoundingShift(UInt.self, rounding: .up) 146 | testRoundingShift(UInt.self, rounding: .towardZero) 147 | testRoundingShift(UInt.self, rounding: .awayFromZero) 148 | testRoundingShift(UInt.self, rounding: .toNearestOrUp) 149 | testRoundingShift(UInt.self, rounding: .toNearestOrDown) 150 | testRoundingShift(UInt.self, rounding: .toNearestOrZero) 151 | testRoundingShift(UInt.self, rounding: .toNearestOrAway) 152 | testRoundingShift(UInt.self, rounding: .toNearestOrEven) 153 | testRoundingShift(UInt.self, rounding: .toOdd) 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /Sources/IntegerUtilities/SaturatingArithmetic.swift: -------------------------------------------------------------------------------- 1 | //===--- SaturatingArithmetic.swift ---------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2023 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | extension FixedWidthInteger { 13 | /// `~0` (all-ones) if this value is negative, otherwise `0`. 14 | /// 15 | /// Note that if `Self` is unsigned, this always returns `0`, 16 | /// but it is useful for writing algorithms that are generic over 17 | /// signed and unsigned integers. 18 | @inline(__always) @usableFromInline 19 | var signbit: Self { 20 | return self < .zero ? ~.zero : .zero 21 | } 22 | 23 | /// Saturating integer addition 24 | /// 25 | /// `self + other` clamped to the representable range of the type. e.g.: 26 | /// ``` 27 | /// let a: Int8 = 84 28 | /// let b: Int8 = 100 29 | /// // 84 + 100 = 184 is not representable as 30 | /// // Int8, so `c` is clamped to Int8.max (127). 31 | /// let c = a.addingWithSaturation(b) 32 | /// ``` 33 | /// 34 | /// If the "normal addition" `self + other` does not trap, 35 | /// this method produces the same result. 36 | @inlinable 37 | public func addingWithSaturation(_ other: Self) -> Self { 38 | let (wrapped, overflow) = addingReportingOverflow(other) 39 | return overflow ? Self.max &- signbit : wrapped 40 | } 41 | 42 | /// Saturating integer subtraction 43 | /// 44 | /// `self - other` clamped to the representable range of the type. e.g.: 45 | /// ``` 46 | /// let a: UInt = 37 47 | /// let b: UInt = 42 48 | /// // 37 - 42 = -5, which is not representable as 49 | /// // UInt, so `c` is clamped to UInt.min (zero). 50 | /// let c = a.subtractingWithSaturation(b) 51 | /// ``` 52 | /// 53 | /// Note that `a.addingWithSaturation(-b)` is not always equivalent to 54 | /// `a.subtractingWithSaturation(b)`, because `-b` is not representable 55 | /// if `b` is the minimum value of a signed type. 56 | /// 57 | /// If the "normal subtraction" `self - other` does not trap, 58 | /// this method produces the same result. 59 | @inlinable 60 | public func subtractingWithSaturation(_ other: Self) -> Self { 61 | let (wrapped, overflow) = subtractingReportingOverflow(other) 62 | if !overflow { return wrapped } 63 | return Self.isSigned ? Self.max &- signbit : 0 64 | } 65 | 66 | /// Saturating integer negation 67 | /// 68 | /// For unsigned types, the result is always zero. This is not very 69 | /// interesting, but may occasionally be useful in generic contexts. 70 | /// For signed types, the result is `-self` unless `self` is `.min`, 71 | /// in which case the result is `.max`. 72 | @inlinable 73 | public func negatedWithSaturation() -> Self { 74 | Self.zero.subtractingWithSaturation(self) 75 | } 76 | 77 | /// Saturating integer multiplication 78 | /// 79 | /// `self * other` clamped to the representable range of the type. e.g.: 80 | /// ``` 81 | /// let a: Int8 = -16 82 | /// let b: Int8 = -8 83 | /// // -16 * -8 = 128 is not representable as 84 | /// // Int8, so `c` is clamped to Int8.max (127). 85 | /// let c = a.multipliedWithSaturation(by: b) 86 | /// ``` 87 | /// 88 | /// If the "normal multiplication" `self * other` does not trap, 89 | /// this method produces the same result. 90 | @inlinable 91 | public func multipliedWithSaturation(by other: Self) -> Self { 92 | let (high, low) = multipliedFullWidth(by: other) 93 | let wrapped = Self(truncatingIfNeeded: low) 94 | if high == wrapped.signbit { return wrapped } 95 | return Self.max &- high.signbit 96 | } 97 | 98 | /// Bitwise left shift with rounding and saturation. 99 | /// 100 | /// `self` multiplied by the rational number 2^(`count`), saturated to the 101 | /// range `Self.min ... Self.max`, and rounded according to `rule`. 102 | /// 103 | /// See `shifted(rightBy:rounding:)`, defined on `BinaryInteger`, for more 104 | /// discussion of rounding shifts with examples. 105 | /// 106 | /// - Parameters: 107 | /// - leftBy count: the number of bits to shift by. If positive, this is 108 | /// a left-shift, and if negative a right shift. 109 | /// - rounding rule: the direction in which to round if `count` is negative. 110 | @inlinable 111 | public func shiftedWithSaturation( 112 | leftBy count: Int, 113 | rounding rule: RoundingRule = .down 114 | ) -> Self { 115 | if count == 0 { return self } 116 | // If count is negative, negate it and do a right shift without 117 | // saturation instead, since we already have that implemented. 118 | guard count > 0 else { 119 | return shifted( 120 | rightBy: count.negatedWithSaturation(), 121 | rounding: rule 122 | ) 123 | } 124 | let clamped = Self.max &- signbit 125 | guard count < Self.bitWidth else { 126 | // If count is bitWidth or greater, we always overflow 127 | // unless self is zero. 128 | return self == 0 ? 0 : clamped 129 | } 130 | // Now we have 0 < count < bitWidth, so we can use a nice 131 | // straightforward implementation; a shift overflows if 132 | // the complementary shift doesn't match sign-or-zero 133 | // extension. E.g.: 134 | // 135 | // - signed 0b0010_1111 << 2 overflows, because 136 | // 0b0010_1111 >> 5 is 0b0000_0001, which does not 137 | // equal 0b0000_0000 138 | // 139 | // - unsigned 0b0010_1111 << 2 does not overflow, 140 | // because 0b0010_0000 >> 6 is 0b0000_0000, which 141 | // does equal 0b0000_0000. 142 | let valueBits = Self.bitWidth &- (Self.isSigned ? 1 : 0) 143 | let wrapped = self &<< count 144 | let complement = valueBits &- count 145 | return self &>> complement == signbit ? wrapped : clamped 146 | } 147 | 148 | /// Bitwise left with rounding and saturation. 149 | /// 150 | /// `self` multiplied by the rational number 2^(`count`), saturated to the 151 | /// range `Self.min ... Self.max`, and rounded according to `rule`. 152 | /// 153 | /// See `shifted(rightBy:rounding:)` for more discussion of rounding 154 | /// shifts with examples. 155 | /// 156 | /// - Parameters: 157 | /// - leftBy count: the number of bits to shift by. If positive, this is a 158 | /// left-shift, and if negative a right shift. 159 | /// - rounding rule: the direction in which to round if `count` is negative. 160 | @_transparent 161 | public func shiftedWithSaturation( 162 | leftBy count: Count, 163 | rounding rule: RoundingRule = .down 164 | ) -> Self { 165 | self.shiftedWithSaturation(leftBy: Int(clamping: count), rounding: rule) 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /Tests/IntegerUtilitiesTests/SaturatingArithmeticTests.swift: -------------------------------------------------------------------------------- 1 | //===--- SaturatingArithmeticTests.swift ----------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2023 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import IntegerUtilities 14 | import XCTest 15 | import _TestSupport 16 | 17 | final class IntegerUtilitiesSaturatingTests: XCTestCase { 18 | 19 | func testSaturatingAddSigned() { 20 | for a in Int8.min ... Int8.max { 21 | for b in Int8.min ... Int8.max { 22 | let expected = Int8(clamping: Int16(a) + Int16(b)) 23 | let observed = a.addingWithSaturation(b) 24 | if expected != observed { 25 | print("Error found in (\(a)).addingWithSaturation(\(b)).") 26 | print("Expected: \(String(expected, radix: 16))") 27 | print("Observed: \(String(observed, radix: 16))") 28 | XCTFail() 29 | return 30 | } 31 | } 32 | } 33 | } 34 | 35 | func testSaturatingSubSigned() { 36 | for a in Int8.min ... Int8.max { 37 | for b in Int8.min ... Int8.max { 38 | let expected = Int8(clamping: Int16(a) - Int16(b)) 39 | let observed = a.subtractingWithSaturation(b) 40 | if expected != observed { 41 | print("Error found in (\(a)).subtractingWithSaturation(\(b)).") 42 | print("Expected: \(String(expected, radix: 16))") 43 | print("Observed: \(String(observed, radix: 16))") 44 | XCTFail() 45 | return 46 | } 47 | } 48 | } 49 | } 50 | 51 | func testSaturatingNegSigned() { 52 | for a in Int8.min ... Int8.max { 53 | let expected = Int8(clamping: 0 - Int16(a)) 54 | let observed = a.negatedWithSaturation() 55 | if expected != observed { 56 | print("Error found in (\(a)).negatedWithSaturation().") 57 | print("Expected: \(String(expected, radix: 16))") 58 | print("Observed: \(String(observed, radix: 16))") 59 | XCTFail() 60 | return 61 | } 62 | } 63 | } 64 | 65 | func testSaturatingMulSigned() { 66 | for a in Int8.min ... Int8.max { 67 | for b in Int8.min ... Int8.max { 68 | let expected = Int8(clamping: Int16(a) * Int16(b)) 69 | let observed = a.multipliedWithSaturation(by: b) 70 | if expected != observed { 71 | print("Error found in (\(a)).multipliedWithSaturation(by: \(b)).") 72 | print("Expected: \(String(expected, radix: 16))") 73 | print("Observed: \(String(observed, radix: 16))") 74 | XCTFail() 75 | return 76 | } 77 | } 78 | } 79 | } 80 | 81 | func testSaturatingAddUnsigned() { 82 | for a in UInt8.min ... UInt8.max { 83 | for b in UInt8.min ... UInt8.max { 84 | let expected = UInt8(clamping: UInt16(a) + UInt16(b)) 85 | let observed = a.addingWithSaturation(b) 86 | if expected != observed { 87 | print("Error found in (\(a)).addingWithSaturation(\(b)).") 88 | print("Expected: \(String(expected, radix: 16))") 89 | print("Observed: \(String(observed, radix: 16))") 90 | XCTFail() 91 | return 92 | } 93 | } 94 | } 95 | } 96 | 97 | func testSaturatingSubUnsigned() { 98 | for a in UInt8.min ... UInt8.max { 99 | for b in UInt8.min ... UInt8.max { 100 | let expected = UInt8(clamping: Int16(a) - Int16(b)) 101 | let observed = a.subtractingWithSaturation(b) 102 | if expected != observed { 103 | print("Error found in (\(a)).subtractingWithSaturation(\(b)).") 104 | print("Expected: \(String(expected, radix: 16))") 105 | print("Observed: \(String(observed, radix: 16))") 106 | XCTFail() 107 | return 108 | } 109 | } 110 | } 111 | } 112 | 113 | func testSaturatingNegUnsigned() { 114 | for a in UInt8.min ... UInt8.max { 115 | let observed = a.negatedWithSaturation() 116 | if 0 != observed { 117 | print("Error found in (\(a)).negatedWithSaturation().") 118 | print("Expected: zero") 119 | print("Observed: \(String(observed, radix: 16))") 120 | XCTFail() 121 | return 122 | } 123 | } 124 | } 125 | 126 | func testSaturatingMulUnsigned() { 127 | for a in UInt8.min ... UInt8.max { 128 | for b in UInt8.min ... UInt8.max { 129 | let expected = UInt8(clamping: UInt16(a) * UInt16(b)) 130 | let observed = a.multipliedWithSaturation(by: b) 131 | if expected != observed { 132 | print("Error found in (\(a)).multipliedWithSaturation(by: \(b)).") 133 | print("Expected: \(String(expected, radix: 16))") 134 | print("Observed: \(String(observed, radix: 16))") 135 | XCTFail() 136 | } 137 | } 138 | } 139 | } 140 | 141 | func testSaturatingShift( 142 | _ value: T, _ count: C, rounding rule: RoundingRule 143 | ) where T: FixedWidthInteger, C: FixedWidthInteger { 144 | let observed = value.shiftedWithSaturation(leftBy: count, rounding: rule) 145 | var expected: T = 0 146 | if count <= 0 { 147 | expected = value.shifted(rightBy: -Int64(count), rounding: rule) 148 | } else { 149 | let multiplier: T = 1 << count 150 | if multiplier <= 0 { 151 | expected = value == 0 ? 0 : 152 | value < 0 ? .min : .max 153 | } else { 154 | expected = value.multipliedWithSaturation(by: multiplier) 155 | } 156 | } 157 | if observed != expected { 158 | print("Error found in \(T.self).shiftedWithSaturation(leftBy: \(count), rounding: \(rule)).") 159 | print(" Value: \(String(value, radix: 16))") 160 | print("Expected: \(String(expected, radix: 16))") 161 | print("Observed: \(String(observed, radix: 16))") 162 | XCTFail() 163 | return 164 | } 165 | } 166 | 167 | func testSaturatingShift( 168 | _ type: T.Type, rounding rule: RoundingRule 169 | ) { 170 | for count in Int8.min ... .max { 171 | testSaturatingShift(0, count, rounding: rule) 172 | for bits in 0 ..< T.bitWidth { 173 | let msb: T.Magnitude = 1 << bits 174 | let value = T(truncatingIfNeeded: msb) | .random(in: 0 ... T(msb-1)) 175 | testSaturatingShift(value, count, rounding: rule) 176 | testSaturatingShift(0 &- value, count, rounding: rule) 177 | } 178 | } 179 | } 180 | 181 | func testSaturatingShifts() { 182 | testSaturatingShift(Int8.self, rounding: .toOdd) 183 | testSaturatingShift(UInt8.self, rounding: .toOdd) 184 | testSaturatingShift(Int.self, rounding: .toOdd) 185 | testSaturatingShift(UInt.self, rounding: .toOdd) 186 | } 187 | 188 | func testEdgeCaseForNegativeCount() { 189 | XCTAssertEqual(1.shiftedWithSaturation(leftBy: Int.min), 0) 190 | } 191 | 192 | } 193 | -------------------------------------------------------------------------------- /Sources/ComplexModule/Complex.swift: -------------------------------------------------------------------------------- 1 | //===--- Complex.swift ----------------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | import RealModule 13 | 14 | // A [complex number](https://en.wikipedia.org/wiki/Complex_number). 15 | // See Documentation.docc/Complex.md for more details. 16 | @frozen 17 | public struct Complex where RealType: Real { 18 | // A note on the `x` and `y` properties 19 | // 20 | // `x` and `y` are the names we use for the raw storage of the real and 21 | // imaginary components of our complex number. We also provide public 22 | // `.real` and `.imaginary` properties, which wrap this storage and 23 | // fixup the semantics for non-finite values. 24 | 25 | /// The storage for the real component of the value. 26 | @usableFromInline @inline(__always) 27 | internal var x: RealType 28 | 29 | /// The storage for the imaginary part of the value. 30 | @usableFromInline @inline(__always) 31 | internal var y: RealType 32 | 33 | /// A complex number constructed by specifying the real and imaginary parts. 34 | @_transparent 35 | public init(_ real: RealType, _ imaginary: RealType) { 36 | x = real 37 | y = imaginary 38 | } 39 | } 40 | 41 | extension Complex: Sendable where RealType: Sendable { } 42 | 43 | // MARK: - Basic properties 44 | extension Complex { 45 | /// The real part of this complex value. 46 | /// 47 | /// If `z` is not finite, `z.real` is `.nan`. 48 | public var real: RealType { 49 | @_transparent 50 | get { isFinite ? x : .nan } 51 | 52 | @_transparent 53 | set { x = newValue } 54 | } 55 | 56 | /// The imaginary part of this complex value. 57 | /// 58 | /// If `z` is not finite, `z.imaginary` is `.nan`. 59 | public var imaginary: RealType { 60 | @_transparent 61 | get { isFinite ? y : .nan } 62 | 63 | @_transparent 64 | set { y = newValue } 65 | } 66 | 67 | /// The raw representation of the value. 68 | /// 69 | /// Use this when you need the underlying RealType values, 70 | /// without fixup for NaN or infinity. 71 | public var rawStorage: (x: RealType, y: RealType) { 72 | @_transparent 73 | get { (x, y) } 74 | @_transparent 75 | set { (x, y) = newValue } 76 | } 77 | 78 | /// The raw representation of the real part of this value. 79 | @available(*, deprecated, message: "Use rawStorage") 80 | @_transparent 81 | public var _rawX: RealType { x } 82 | 83 | /// The raw representation of the imaginary part of this value. 84 | @available(*, deprecated, message: "Use rawStorage") 85 | @_transparent 86 | public var _rawY: RealType { y } 87 | } 88 | 89 | extension Complex { 90 | /// The imaginary unit. 91 | /// 92 | /// See also ``zero``, ``one`` and ``infinity``. 93 | @_transparent 94 | public static var i: Complex { 95 | Complex(0, 1) 96 | } 97 | 98 | /// The point at infinity. 99 | /// 100 | /// See also ``zero``, ``one`` and ``i``. 101 | @_transparent 102 | public static var infinity: Complex { 103 | Complex(.infinity, 0) 104 | } 105 | 106 | /// True if this value is finite. 107 | /// 108 | /// A complex value is finite if neither component is an infinity or nan. 109 | /// 110 | /// See also ``isNormal``, ``isSubnormal`` and ``isZero``. 111 | @_transparent 112 | public var isFinite: Bool { 113 | x.isFinite && y.isFinite 114 | } 115 | 116 | /// True if this value is normal. 117 | /// 118 | /// A complex number is normal if it is finite and *either* the real or 119 | /// imaginary component is normal. A floating-point number representing 120 | /// one of the components is normal if its exponent allows a full-precision 121 | /// representation. 122 | /// 123 | /// See also ``isFinite``, ``isSubnormal`` and ``isZero``. 124 | @_transparent 125 | public var isNormal: Bool { 126 | isFinite && (x.isNormal || y.isNormal) 127 | } 128 | 129 | /// True if this value is subnormal. 130 | /// 131 | /// A complex number is subnormal if it is finite, not normal, and not zero. 132 | /// When the result of a computation is subnormal, underflow has occurred and 133 | /// the result generally does not have full precision. 134 | /// 135 | /// See also ``isFinite``, ``isNormal`` and ``isZero``. 136 | @_transparent 137 | public var isSubnormal: Bool { 138 | isFinite && !isNormal && !isZero 139 | } 140 | 141 | /// True if this value is zero. 142 | /// 143 | /// A complex number is zero if *both* the real and imaginary components 144 | /// are zero. 145 | /// 146 | /// See also ``isFinite``, ``isNormal`` and ``isSubnormal``. 147 | @_transparent 148 | public var isZero: Bool { 149 | x == 0 && y == 0 150 | } 151 | 152 | /// A "canonical" representation of the value. 153 | /// 154 | /// For normal complex numbers with a RealType conforming to 155 | /// BinaryFloatingPoint (the common case), the result is simply this value 156 | /// unmodified. For zeros, the result has the representation (+0, +0). For 157 | /// infinite values, the result has the representation (+inf, +0). 158 | /// 159 | /// If the RealType admits non-canonical representations, the x and y 160 | /// components are canonicalized in the result. 161 | /// 162 | /// This is mainly useful for interoperation with other languages, where 163 | /// you may want to reduce each equivalence class to a single representative 164 | /// before passing across language boundaries, but it may also be useful 165 | /// for some serialization tasks. It's also a useful implementation detail 166 | /// for some primitive operations. 167 | @_transparent 168 | public var canonicalized: Self { 169 | if isZero { return .zero } 170 | if isFinite { return self.multiplied(by: 1) } 171 | return .infinity 172 | } 173 | } 174 | 175 | // MARK: - Additional Initializers 176 | extension Complex { 177 | /// The complex number with specified real part and zero imaginary part. 178 | /// 179 | /// Equivalent to `Complex(real, 0)`. 180 | @inlinable 181 | public init(_ real: RealType) { 182 | self.init(real, 0) 183 | } 184 | 185 | /// The complex number with zero real part and specified imaginary part. 186 | /// 187 | /// Equivalent to `Complex(0, imaginary)`. 188 | @inlinable 189 | public init(imaginary: RealType) { 190 | self.init(0, imaginary) 191 | } 192 | } 193 | 194 | extension Complex where RealType: BinaryFloatingPoint { 195 | /// `other` rounded to the nearest representable value of this type. 196 | @inlinable 197 | public init(_ other: Complex) { 198 | self.init(RealType(other.x), RealType(other.y)) 199 | } 200 | 201 | /// `other`, if it can be represented exactly in this type; otherwise `nil`. 202 | @inlinable 203 | public init?(exactly other: Complex) { 204 | guard let x = RealType(exactly: other.x), 205 | let y = RealType(exactly: other.y) else { return nil } 206 | self.init(x, y) 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swift Numerics 2 | 3 | ## Introduction 4 | 5 | Swift Numerics provides a set of modules that support numerical computing in Swift. 6 | These modules fall broadly into two categories: 7 | 8 | - API that is too specialized to go into the standard library, but which is sufficiently general to be centralized in a single common package. 9 | - API that is under active development toward possible future inclusion in the standard library. 10 | 11 | There is some overlap between these two categories, and an API that begins in the first category may migrate into the second as it matures and new uses are discovered. 12 | 13 | Swift Numerics modules are fine-grained. 14 | For example, if you need support for Complex numbers, you can import ComplexModule[^1] as a standalone module: 15 | 16 | ```swift 17 | import ComplexModule 18 | 19 | let z = Complex.i 20 | ``` 21 | 22 | There is also a top-level `Numerics` module that re-exports the complete public interface of Swift Numerics: 23 | 24 | ```swift 25 | import Numerics 26 | 27 | // The entire Swift Numerics API is now available 28 | ``` 29 | 30 | Swift Numerics modules have minimal dependencies on other projects. 31 | 32 | The current modules assume only the availability of the Swift and C standard libraries and the runtime support provided by compiler-rt. 33 | 34 | Future expansion may assume the availability of other standard interfaces, such as [BLAS (Basic Linear Algebra Subprograms)](https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms) and [LAPACK (Linear Algebra Package)](https://en.wikipedia.org/wiki/LAPACK), but modules with more specialized dependencies (or dependencies that are not available on all platforms supported by Swift) belong in a separate package. 35 | 36 | Because we intend to make it possible to adopt Swift Numerics modules in the standard library at some future point, Swift Numerics uses the same license and contribution guidelines as the Swift project. 37 | 38 | ## Using Swift Numerics in your project 39 | 40 | To use Swift Numerics in a SwiftPM project: 41 | 42 | 1. Add the following line to the dependencies in your `Package.swift` file: 43 | 44 | ```swift 45 | .package(url: "https://github.com/apple/swift-numerics", from: "1.0.0"), 46 | ``` 47 | 48 | 2. Add `Numerics` as a dependency for your target: 49 | 50 | ```swift 51 | .target(name: "MyTarget", dependencies: [ 52 | .product(name: "Numerics", package: "swift-numerics"), 53 | "AnotherModule" 54 | ]), 55 | ``` 56 | 57 | 3. Add `import Numerics` in your source code. 58 | 59 | ## Source stability 60 | 61 | The Swift Numerics package is source stable; version numbers follow [Semantic Versioning](https://semver.org). 62 | The public API of the `swift-numerics` package consists of non-underscored declarations that are marked either `public` or `usableFromInline` in modules re-exported by the top-level `Numerics` module. 63 | Interfaces that aren't part of the public API may continue to change in any release, including patch releases. 64 | 65 | Note that contents of the `_NumericsShims` and `_TestSupport` modules, as well as contents of the `Tests` directory, explicitly are not public API. 66 | The definitions therein may therefore change at whim, and the entire module may be removed in any new release. 67 | If you have a use case that requires underscored operations, please raise an issue to request that they be made public API. 68 | 69 | Future minor versions of the package may introduce changes to these rules as needed. 70 | 71 | We'd like this package to quickly embrace Swift language and toolchain improvements that are relevant to its mandate. 72 | Accordingly, from time to time, we expect that new versions of this package will require clients to upgrade to a more recent Swift toolchain release. 73 | Requiring a new Swift release will only require a minor version bump. 74 | 75 | ## Contributing to Swift Numerics 76 | 77 | Swift Numerics is a standalone library that is separate from the core Swift project, but it will sometimes act as a staging ground for APIs that will later be incorporated into the Swift Standard Library. 78 | When that happens, such changes will be proposed to the Swift Standard Library using the established evolution process of the Swift project. 79 | 80 | Swift Numerics uses GitHub issues to track bugs and features. We use pull requests for development. 81 | 82 | ### How to propose a new module 83 | 84 | 1. Raise an issue with the [new module] tag. 85 | 2. Raise a PR with an implementation sketch. 86 | 3. Once you have some consensus, ask an admin to create a feature branch against which PRs can be raised. 87 | 4. When the design has stabilized and is functional enough to be useful, raise a PR to merge the new module to main. 88 | 89 | ### How to propose a new feature for an existing module 90 | 91 | 1. Raise an issue with the [enhancement] tag. 92 | 2. Raise a PR with your implementation, and discuss the implementation there. 93 | 3. Once there is a consensus that the new feature is desirable and the design is suitable, it can be merged. 94 | 95 | ### How to fix a bug, or make smaller improvements 96 | 97 | 1. Raise a PR with your change. 98 | 2. Make sure to add test coverage for whatever changes you are making. 99 | 100 | ### Forums 101 | 102 | Questions about how to use Swift Numerics modules, or issues that are not clearly bugs can be discussed in the ["Swift Numerics" section of the Swift forums](https://forums.swift.org/c/related-projects/swift-numerics). 103 | 104 | ## Modules 105 | 106 | 1. [`RealModule`](Sources/RealModule/README.md) 107 | 2. [`ComplexModule`](Sources/ComplexModule/README.md) 108 | 3. [`IntegerUtilities`](Sources/IntegerUtilities/README.md) (on main only, not yet present in a released tag) 109 | 110 | ## Future expansion 111 | 112 | 1. [Large Fixed-Width Integers](https://github.com/apple/swift-numerics/issues/4) 113 | 2. [Arbitrary-Precision Integers](https://github.com/apple/swift-numerics/issues/5) 114 | 3. [Shaped Arrays](https://github.com/apple/swift-numerics/issues/6) 115 | 4. [Decimal Floating-point](https://github.com/apple/swift-numerics/issues/7) 116 | 117 | [^1]: The module is named `ComplexModule` instead of `Complex` because Swift is currently unable to use the fully-qualified name for types when a type and module have the same name (discussion here: https://forums.swift.org/t/pitch-fully-qualified-name-syntax/28482). 118 | This would prevent users of Swift Numerics who don't need generic types from doing things such as: 119 | 120 | ```swift 121 | import Complex 122 | // I know I only ever want Complex, so I shouldn't need the generic parameter. 123 | typealias Complex = Complex.Complex // This doesn't work, because name lookup fails. 124 | ``` 125 | 126 | For this reason, modules that would have this ambiguity are suffixed with `Module` within Swift Numerics: 127 | 128 | ```swift 129 | import ComplexModule 130 | // I know I only ever want Complex, so I shouldn't need the generic parameter. 131 | typealias Complex = ComplexModule.Complex 132 | // But I can still refer to the generic type by qualifying the name if I need it occasionally: 133 | let a = ComplexModule.Complex 134 | ``` 135 | 136 | The `Real` module does not contain a `Real` type, but does contain a `Real` protocol. 137 | Users may want to define their own `Real` type (and possibly re-export the `Real` module)--that is why the suffix is also applied there. 138 | New modules have to evaluate this decision carefully, but can err on the side of adding the suffix. 139 | It's expected that most users will simply `import Numerics`, so this isn't an issue for them. 140 | -------------------------------------------------------------------------------- /Sources/RealModule/Double+Real.swift: -------------------------------------------------------------------------------- 1 | //===--- Double+Real.swift ------------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | import _NumericsShims 13 | 14 | extension Double: Real { 15 | @_transparent 16 | public static func cos(_ x: Double) -> Double { 17 | libm_cos(x) 18 | } 19 | 20 | @_transparent 21 | public static func sin(_ x: Double) -> Double { 22 | libm_sin(x) 23 | } 24 | 25 | @_transparent 26 | public static func tan(_ x: Double) -> Double { 27 | libm_tan(x) 28 | } 29 | 30 | @_transparent 31 | public static func acos(_ x: Double) -> Double { 32 | libm_acos(x) 33 | } 34 | 35 | @_transparent 36 | public static func asin(_ x: Double) -> Double { 37 | libm_asin(x) 38 | } 39 | 40 | @_transparent 41 | public static func atan(_ x: Double) -> Double { 42 | libm_atan(x) 43 | } 44 | 45 | @_transparent 46 | public static func cosh(_ x: Double) -> Double { 47 | libm_cosh(x) 48 | } 49 | 50 | @_transparent 51 | public static func sinh(_ x: Double) -> Double { 52 | libm_sinh(x) 53 | } 54 | 55 | @_transparent 56 | public static func tanh(_ x: Double) -> Double { 57 | libm_tanh(x) 58 | } 59 | 60 | @_transparent 61 | public static func acosh(_ x: Double) -> Double { 62 | libm_acosh(x) 63 | } 64 | 65 | @_transparent 66 | public static func asinh(_ x: Double) -> Double { 67 | libm_asinh(x) 68 | } 69 | 70 | @_transparent 71 | public static func atanh(_ x: Double) -> Double { 72 | libm_atanh(x) 73 | } 74 | 75 | @_transparent 76 | public static func exp(_ x: Double) -> Double { 77 | libm_exp(x) 78 | } 79 | 80 | @_transparent 81 | public static func expMinusOne(_ x: Double) -> Double { 82 | libm_expm1(x) 83 | } 84 | 85 | @_transparent 86 | public static func log(_ x: Double) -> Double { 87 | libm_log(x) 88 | } 89 | 90 | @_transparent 91 | public static func log(onePlus x: Double) -> Double { 92 | libm_log1p(x) 93 | } 94 | 95 | @_transparent 96 | public static func erf(_ x: Double) -> Double { 97 | libm_erf(x) 98 | } 99 | 100 | @_transparent 101 | public static func erfc(_ x: Double) -> Double { 102 | libm_erfc(x) 103 | } 104 | 105 | @_transparent 106 | public static func exp2(_ x: Double) -> Double { 107 | libm_exp2(x) 108 | } 109 | 110 | #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) 111 | @_transparent 112 | public static func exp10(_ x: Double) -> Double { 113 | libm_exp10(x) 114 | } 115 | #endif 116 | 117 | #if os(macOS) && arch(x86_64) 118 | // Workaround for macOS bug () where hypot can 119 | // overflow for values very close to the overflow boundary of the naive 120 | // algorithm. Since this is only for macOS, we can just unconditionally 121 | // use Float80, which makes the implementation trivial. 122 | public static func hypot(_ x: Double, _ y: Double) -> Double { 123 | if x.isInfinite || y.isInfinite { return .infinity } 124 | let x80 = Float80(x) 125 | let y80 = Float80(y) 126 | return Double(Float80.sqrt(x80*x80 + y80*y80)) 127 | } 128 | #else 129 | @_transparent 130 | public static func hypot(_ x: Double, _ y: Double) -> Double { 131 | libm_hypot(x, y) 132 | } 133 | #endif 134 | 135 | @_transparent 136 | public static func gamma(_ x: Double) -> Double { 137 | libm_tgamma(x) 138 | } 139 | 140 | @_transparent 141 | public static func log2(_ x: Double) -> Double { 142 | libm_log2(x) 143 | } 144 | 145 | @_transparent 146 | public static func log10(_ x: Double) -> Double { 147 | libm_log10(x) 148 | } 149 | 150 | @_transparent 151 | public static func pow(_ x: Double, _ y: Double) -> Double { 152 | guard x >= 0 else { return .nan } 153 | if x == 0 && y == 0 { return .nan } 154 | return libm_pow(x, y) 155 | } 156 | 157 | @_transparent 158 | public static func pow(_ x: Double, _ n: Int) -> Double { 159 | // If n is exactly representable as Double, we can just call pow: 160 | // Note that all calls on a 32b platform go down this path. 161 | if let y = Double(exactly: n) { return libm_pow(x, y) } 162 | // n is not representable in Double, so we will split it into two parts, 163 | // low and high, such that (high + low) = n, and use the identity: 164 | // 165 | // x**(high + low) = x**high * x**low. 166 | // 167 | // We put the high-order 32 bits into high, and the remaining 32 bits 168 | // in low. 169 | // 170 | // The exact split isn't important; all we need is that both pieces get 171 | // less than 53 bits (so that they are exact) and that they both have 172 | // the same sign as n. 173 | // 174 | // This second point is a little bit subtle--why is 175 | // it necessary? Consider what would happen if we took x = 2 and 176 | // n = Int.min + Int(UInt32.max), and simply naively split n without 177 | // taking care with the sign. We would end up computing: 178 | // 179 | // 2**n = 2**Int.min * 2**UInt32.max 180 | // 181 | // The first exponent is negative, the second positive, so the first term 182 | // underflows to zero, and the second overflows to infinity, so the final 183 | // result is NaN, when it should be zero. In order to avoid this 184 | // situation, we make sure that high contains n rounded *towards zero*, 185 | // rather than using simple two's-complement truncation (which rounds 186 | // down). 187 | let mask = Int(truncatingIfNeeded: UInt32.max) 188 | let round = n < 0 ? mask : 0 189 | // The addition and subtraction below cannot actually overflow (proof: 190 | // round is positive if n is negative, and zero otherwise, so n + round 191 | // is guaranteed to be representable, and n and high have the same sign, 192 | // so n - high is also representable), but it's hard to tell the compiler 193 | // that, so I'm using wrapping operations instead. 194 | let high = (n &+ round) & ~mask 195 | let low = n &- high 196 | return libm_pow(x, Double(low)) * libm_pow(x, Double(high)) 197 | } 198 | 199 | @_transparent 200 | public static func root(_ x: Double, _ n: Int) -> Double { 201 | guard x >= 0 || n % 2 != 0 else { return .nan } 202 | // Workaround the issue mentioned below for the specific case of n = 3 203 | // where we can fallback on cbrt. 204 | if n == 3 { return libm_cbrt(x) } 205 | // TODO: this implementation is not quite correct, because either n or 206 | // 1/n may be not be representable as Double. 207 | return Double(signOf: x, magnitudeOf: libm_pow(x.magnitude, 1/Double(n))) 208 | } 209 | 210 | @_transparent 211 | public static func atan2(y: Double, x: Double) -> Double { 212 | libm_atan2(y, x) 213 | } 214 | 215 | #if !os(Windows) 216 | @_transparent 217 | public static func logGamma(_ x: Double) -> Double { 218 | var dontCare: Int32 = 0 219 | return libm_lgamma(x, &dontCare) 220 | } 221 | #endif 222 | 223 | @_transparent 224 | public static func _relaxedAdd(_ a: Double, _ b: Double) -> Double { 225 | _numerics_relaxed_add(a, b) 226 | } 227 | 228 | @_transparent 229 | public static func _relaxedMul(_ a: Double, _ b: Double) -> Double { 230 | _numerics_relaxed_mul(a, b) 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /Tests/RealTests/ElementaryFunctionChecks.swift: -------------------------------------------------------------------------------- 1 | //===--- ElementaryFunctionChecks.swift ------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | import XCTest 13 | import RealModule 14 | import _TestSupport 15 | 16 | #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android) 17 | public typealias TestLiteralType = Float80 18 | #else 19 | public typealias TestLiteralType = Double 20 | #endif 21 | 22 | @discardableResult 23 | internal func assertClose( 24 | _ expected: TestLiteralType, 25 | _ observed: T, 26 | allowedError: T = 16, 27 | file: StaticString = #file, 28 | line: UInt = #line 29 | ) -> T where T: BinaryFloatingPoint { 30 | // Shortcut relative-error check if we got the sign wrong; it's OK to 31 | // underflow to zero, but we do not want to allow going right through 32 | // zero and getting the sign wrong. 33 | guard observed.sign == expected.sign else { 34 | print("Sign was wrong: expected \(expected) but saw \(observed).") 35 | XCTFail(file: file, line: line) 36 | return .infinity 37 | } 38 | if observed.isNaN && expected.isNaN { return 0 } 39 | // If T(expected) is zero or infinite, and matches observed, the error 40 | // is zero. 41 | let expectedT = T(expected) 42 | if observed.isZero && expectedT.isZero { return 0 } 43 | if observed.isInfinite && expectedT.isInfinite { return 0 } 44 | // Special-case where only one of expectedT or observed is infinity. 45 | // Artificially knock everything down a binade, treat actual infinity as 46 | // the base of the next binade up. 47 | func topBinade(signOf x: T) -> T { 48 | T(signOf: x, magnitudeOf: T.greatestFiniteMagnitude.binade) 49 | } 50 | if observed.isInfinite { 51 | return assertClose( 52 | expected/2, topBinade(signOf: observed), 53 | allowedError: allowedError, file: file, line: line 54 | ) 55 | } 56 | if expectedT.isInfinite { 57 | return assertClose( 58 | TestLiteralType(topBinade(signOf: expectedT)), observed/2, 59 | allowedError: allowedError, file: file, line: line 60 | ) 61 | } 62 | // Compute error in ulp, compare to tolerance. 63 | let absoluteError = (TestLiteralType(observed) - expected).magnitude 64 | let scale = max(expectedT.magnitude, T.leastNormalMagnitude).ulp 65 | let ulps = T(absoluteError/TestLiteralType(scale)) 66 | if ulps > allowedError { 67 | print("ULP error was unacceptably large: expected \(expected) but saw \(observed) (\(ulps)-ulp error).") 68 | XCTFail(file: file, line: line) 69 | } 70 | return ulps 71 | } 72 | 73 | internal func assertClose( 74 | _ expected: TestLiteralType, 75 | _ observed: T, 76 | allowedError: T = 16, 77 | worstError: inout T, 78 | file: StaticString = #file, 79 | line: UInt = #line 80 | ) where T: BinaryFloatingPoint { 81 | worstError = max(worstError, assertClose( 82 | expected, observed, allowedError: allowedError, file: file, line: line 83 | )) 84 | } 85 | 86 | internal extension ElementaryFunctions where Self: BinaryFloatingPoint { 87 | static func elementaryFunctionChecks() { 88 | assertClose(1.1863995522992575361931268186727044683, Self.acos(0.375)) 89 | assertClose(0.3843967744956390830381948729670469737, Self.asin(0.375)) 90 | assertClose(0.3587706702705722203959200639264604997, Self.atan(0.375)) 91 | assertClose(0.9305076219123142911494767922295555080, Self.cos(0.375)) 92 | assertClose(0.3662725290860475613729093517162641571, Self.sin(0.375)) 93 | assertClose(0.3936265759256327582294137871012180981, Self.tan(0.375)) 94 | assertClose(0.4949329230945269058895630995767185785, Self.acosh(1.125)) 95 | assertClose(0.9670596312833237113713762009167286709, Self.asinh(1.125)) 96 | assertClose(0.7331685343967135223291211023213964500, Self.atanh(0.625)) 97 | assertClose(1.0711403467045867672994980155670160493, Self.cosh(0.375)) 98 | assertClose(0.3838510679136145687542956764205024589, Self.sinh(0.375)) 99 | assertClose(0.3583573983507859463193602315531580424, Self.tanh(0.375)) 100 | assertClose(1.4549914146182013360537936919875185083, Self.exp(0.375)) 101 | assertClose(0.4549914146182013360537936919875185083, Self.expMinusOne(0.375)) 102 | assertClose(-0.980829253011726236856451127452003999, Self.log(0.375)) 103 | assertClose(0.3184537311185346158102472135905995955, Self.log(onePlus: 0.375)) 104 | assertClose(-0.7211247851537041911608191553900547941, Self.root(-0.375, 3)) 105 | XCTAssertEqual(-10, Self.root(-1000, 3)) 106 | assertClose(0.6123724356957945245493210186764728479, Self.sqrt(0.375)) 107 | assertClose(0.54171335479545025876069682133938570, Self.pow(0.375, 0.625)) 108 | assertClose(-0.052734375, Self.pow(-0.375, 3)) 109 | } 110 | } 111 | 112 | internal extension Real where Self: BinaryFloatingPoint { 113 | static func realFunctionChecks() { 114 | assertClose(1.2968395546510096659337541177924511598, Self.exp2(0.375)) 115 | assertClose(2.3713737056616552616517527574788898386, Self.exp10(0.375)) 116 | assertClose(-1.415037499278843818546261056052183491, Self.log2(0.375)) 117 | assertClose(-0.425968732272281148346188780918363771, Self.log10(0.375)) 118 | assertClose(0.54041950027058415544357836460859991, Self.atan2(y: 0.375, x: 0.625)) 119 | assertClose(0.72886898685566255885926910969319788, Self.hypot(0.375, 0.625)) 120 | assertClose(0.4041169094348222983238250859191217675, Self.erf(0.375)) 121 | assertClose(0.5958830905651777016761749140808782324, Self.erfc(0.375)) 122 | assertClose(2.3704361844166009086464735041766525098, Self.gamma(0.375)) 123 | #if !os(Windows) 124 | assertClose( -0.11775527074107877445136203331798850, Self.logGamma(1.375)) 125 | XCTAssertEqual(.plus, Self.signGamma(1.375)) 126 | XCTAssertEqual(.minus, Self.signGamma(-2.375)) 127 | #endif 128 | } 129 | } 130 | 131 | extension Real { 132 | static func powZeroChecks() { 133 | // pow(_:Self,_:Self) is defined by exp(y log(x)) and has edge-cases to 134 | // match. In particular, if x is zero, log(x) is -infinity, so pow(0,0) 135 | // is exp(0 * -infinity) = exp(nan) = nan. 136 | XCTAssertEqual(pow(0, -1 as Self), infinity) 137 | XCTAssert(pow(0, 0 as Self).isNaN) 138 | XCTAssertEqual(pow(0, 1 as Self), zero) 139 | // pow(_:Self,_:Int) is defined by repeated multiplication or division, 140 | // and hence pow(0, 0) is 1. 141 | XCTAssertEqual(pow(0, -1), infinity) 142 | XCTAssertEqual(pow(0, 0), 1) 143 | XCTAssertEqual(pow(0, 1), zero) 144 | } 145 | } 146 | 147 | final class ElementaryFunctionChecks: XCTestCase { 148 | 149 | #if !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64)) 150 | func testFloat16() { 151 | if #available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) { 152 | Float16.elementaryFunctionChecks() 153 | Float16.realFunctionChecks() 154 | Float16.powZeroChecks() 155 | } 156 | } 157 | #endif 158 | 159 | func testFloat() { 160 | Float.elementaryFunctionChecks() 161 | Float.realFunctionChecks() 162 | Float.powZeroChecks() 163 | } 164 | 165 | func testDouble() { 166 | Double.elementaryFunctionChecks() 167 | Double.realFunctionChecks() 168 | Double.powZeroChecks() 169 | } 170 | 171 | #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android) 172 | func testFloat80() { 173 | Float80.elementaryFunctionChecks() 174 | Float80.realFunctionChecks() 175 | Float80.powZeroChecks() 176 | } 177 | #endif 178 | } 179 | -------------------------------------------------------------------------------- /Sources/IntegerUtilities/ShiftWithRounding.swift: -------------------------------------------------------------------------------- 1 | //===--- ShiftWithRounding.swift ------------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2021 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | extension BinaryInteger { 13 | /// `self` divided by 2^(`count`), rounding the result according to `rule`. 14 | /// 15 | /// The default rounding rule is ``RoundingRule/down``, which matches the 16 | /// behavior of the `>>` operator from the standard library. 17 | /// 18 | /// Some examples of different rounding rules: 19 | /// 20 | /// // 3/2 is 1.5, which rounds (down by default) to 1. 21 | /// 3.shifted(rightBy: 1) 22 | /// 23 | /// // 1.5 rounds up to 2. 24 | /// 3.shifted(rightBy: 1, rounding: .up) 25 | /// 26 | /// // The two closest values are 1 and 2, 1 is returned because it 27 | /// // is odd. 28 | /// 3.shifted(rightBy: 1, rounding: .toOdd) 29 | /// 30 | /// // 7/4 = 1.75, so the result is 1 with probability 1/4, or 2 31 | /// // with probability 3/4. 32 | /// 7.shifted(rightBy: 2, rounding: .stochastically) 33 | /// 34 | /// // 4/4 is exactly 1, so this does not trap. 35 | /// 4.shifted(rightBy: 2, rounding: .requireExact) 36 | /// 37 | /// // 5/2 is 2.5, which is not an integer, so this traps. 38 | /// 5.shifted(rightBy: 1, rounding: .requireExact) 39 | /// 40 | /// When `Self(1) << count` is positive, the following are equivalent: 41 | /// 42 | /// a.shifted(rightBy: count, rounding: rule) 43 | /// a.divided(by: 1 << count, rounding: rule) 44 | @inlinable 45 | public func shifted( 46 | rightBy count: Int, 47 | rounding rule: RoundingRule = .down 48 | ) -> Self { 49 | // Easiest case: count is zero or negative, so shift is always exact; 50 | // delegate to the normal >> operator. 51 | if count <= 0 { return self >> count } 52 | if count >= bitWidth { 53 | // Note: what follows would cause an infinite loop if bitWidth <= 1. 54 | // This will essentially never happen, but in the highly unlikely event 55 | // that we encounter such a case, we promote to Int8, do the shift, and 56 | // then convert back to the appropriate result type. 57 | if bitWidth <= 1 { 58 | return Self(Int8(self).shifted(rightBy: count, rounding: rule)) 59 | } 60 | // That pathological case taken care of, we can now handle over-wide 61 | // shifts by first shifting all but bitWidth - 1 bits with sticky 62 | // rounding, and then shifting the remaining bitWidth - 1 bits with 63 | // the desired rounding mode. 64 | let count = count - (bitWidth - 1) 65 | let floor = self >> count 66 | let lost = self - (floor << count) 67 | let sticky = floor | (lost == 0 ? 0 : 1) 68 | return sticky.shifted(rightBy: bitWidth - 1, rounding: rule) 69 | } 70 | // Now we are in the happy case: 0 < count < bitWidth, which makes all 71 | // the math to handle rounding simpler. 72 | // 73 | // TODO: If we were really, really careful about overflow, some of these 74 | // could be made simpler. E.g. mathematically round up is implemented here 75 | // via: 76 | // 77 | // floor = self >> count 78 | // mask = 1 << count - 1 79 | // lost = self & mask 80 | // return floor + (lost == 0 ? 0 : 1) 81 | // 82 | // _if_ we didn't have to worry about intermediate overflow (either because 83 | // self is bounded away from .max or because we don't have a fixed-width 84 | // type), then we could use the following instead: 85 | // 86 | // mask = 1 << count - 1 87 | // return (self + mask) >> count 88 | // 89 | // However, self + mask can overflow, e.g. if we're shifting .max by 1: 90 | // 91 | // mask = 1 << 1 - 1 = 1 92 | // self + mask = .max + 1 // uh-oh 93 | // 94 | // This cannot occur for arbitrary-precision numbers, so we could use 95 | // the simpler expressions for those (but those are precisely the cases 96 | // where performance of rounding does not matter much, because shifts 97 | // get swamped by even basic arithmetic). More interesting, we could 98 | // promote fixed-width numbers to a wider type, preventing this 99 | // intermediate overflow and allowing us to use the simpler expressions 100 | // much of the time. We could also explicitly _detect_ the overflow if 101 | // it happens and patch up the result, though this is a little bit tricky 102 | // because addingReportingOverflow and friends do not exist on 103 | // BinaryInteger. Hence, this is a TODO for the future. 104 | let mask = Magnitude(1) << count - 1 105 | let lost = Magnitude(truncatingIfNeeded: self) & mask 106 | let floor = self >> count 107 | let ceiling = floor + (lost == 0 ? 0 : 1) 108 | let half: Magnitude = (1 as Magnitude) << (count &- 1) 109 | switch rule { 110 | case .down: 111 | return floor 112 | case .up: 113 | return ceiling 114 | case .towardZero: 115 | return self > 0 ? floor : ceiling 116 | case .awayFromZero: 117 | return self < 0 ? floor : ceiling 118 | case .toNearestOrDown: 119 | return floor + Self((lost + (half - 1)) >> count) 120 | case .toNearestOrUp: 121 | return floor + Self((lost + half) >> count) 122 | case .toNearestOrZero: 123 | let round = half - (self < 0 ? 0 : 1) 124 | return floor + Self((round + lost) >> count) 125 | case .toNearestOrAway: 126 | let round = half - (self > 0 ? 0 : 1) 127 | return floor + Self((round + lost) >> count) 128 | case .toNearestOrEven: 129 | let round = mask >> 1 + Magnitude(floor & 1) 130 | return floor + Self((round + lost) >> count) 131 | case .toOdd: 132 | return floor | (lost == 0 ? 0 : 1) 133 | case .requireExact: 134 | precondition(lost == 0, "shift was not exact.") 135 | return floor 136 | } 137 | } 138 | 139 | /// `self` divided by 2^(`count`), rounding the result according to `rule`. 140 | /// 141 | /// The default rounding rule is ``RoundingRule/down``, which matches the 142 | /// behavior of the `>>` operator from the standard library. 143 | /// 144 | /// Some examples of different rounding rules: 145 | /// 146 | /// // 3/2 is 1.5, which rounds (down by default) to 1. 147 | /// 3.shifted(rightBy: 1) 148 | /// 149 | /// // 1.5 rounds up to 2. 150 | /// 3.shifted(rightBy: 1, rounding: .up) 151 | /// 152 | /// // The two closest values are 1 and 2, 1 is returned because it 153 | /// // is odd. 154 | /// 3.shifted(rightBy: 1, rounding: .toOdd) 155 | /// 156 | /// // 7/4 = 1.75, so the result is 1 with probability 1/4, or 2 157 | /// // with probability 3/4. 158 | /// 7.shifted(rightBy: 2, rounding: .stochastically) 159 | /// 160 | /// // 4/4 is exactly 1, so this does not trap. 161 | /// 4.shifted(rightBy: 2, rounding: .requireExact) 162 | /// 163 | /// // 5/2 is 2.5, which is not an integer, so this traps. 164 | /// 5.shifted(rightBy: 1, rounding: .requireExact) 165 | /// 166 | /// When `Self(1) << count` is positive, the following are equivalent: 167 | /// 168 | /// a.shifted(rightBy: count, rounding: rule) 169 | /// a.divided(by: 1 << count, rounding: rule) 170 | @_transparent 171 | public func shifted( 172 | rightBy count: Count, 173 | rounding rule: RoundingRule = .down 174 | ) -> Self { 175 | self.shifted(rightBy: Int(clamping: count), rounding: rule) 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /Sources/ComplexModule/Complex+AlgebraicField.swift: -------------------------------------------------------------------------------- 1 | //===--- Complex+AlgebraicField.swift -------------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2019-2025 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // 10 | //===----------------------------------------------------------------------===// 11 | 12 | import RealModule 13 | 14 | extension Complex: AlgebraicField { 15 | /// The multiplicative identity `1 + 0i`. 16 | /// 17 | /// See also: ``zero``, ``i``, ``infinity`` 18 | @_transparent 19 | public static var one: Complex { 20 | Complex(1, 0) 21 | } 22 | 23 | /// The [complex conjugate][conj] of this value. 24 | /// 25 | /// [conj]: https://en.wikipedia.org/wiki/Complex_conjugate 26 | @_transparent 27 | public var conjugate: Complex { 28 | Complex(x, -y) 29 | } 30 | 31 | @_transparent 32 | public static func /=(z: inout Complex, w: Complex) { 33 | z = z / w 34 | } 35 | 36 | @_transparent 37 | public static func /(z: Complex, w: Complex) -> Complex { 38 | // Try the naive expression z/w = z * (conj(w) / |w|^2); if we can 39 | // compute this without over/underflow, everything is fine and the 40 | // result is correct. If not, we have to rescale and do the 41 | // computation carefully (see below). 42 | let lenSq = w.lengthSquared 43 | guard lenSq.isNormal else { return rescaledDivide(z, w) } 44 | return z * w.conjugate.divided(by: lenSq) 45 | } 46 | 47 | @usableFromInline @_alwaysEmitIntoClient @inline(never) 48 | internal static func rescaledDivide(_ z: Complex, _ w: Complex) -> Complex { 49 | if w.isZero { return .infinity } 50 | if !w.isFinite { return .zero } 51 | // Scaling algorithm adapted from Doug Priest's "Efficient Scaling for 52 | // Complex Division": 53 | if w.magnitude < .leastNormalMagnitude { 54 | // A difference from Priest's algorithm is that he didn't have to worry 55 | // about types like Float16, where the significand width is comparable 56 | // to the exponent range, such that |leastNormalMagnitude|^(-¾) isn't 57 | // representable (e.g. for Float16 it would want to be 2¹⁸, but the 58 | // largest allowed exponent is 15). Note that it's critical to use zʹ/wʹ 59 | // after rescaling to avoid this, rather than falling through into the 60 | // normal rescaling, because otherwise we might end up back in the 61 | // situation where |w| ~ 1. 62 | let s = 1/(RealType(RealType.radix) * .leastNormalMagnitude) 63 | let wʹ = w.multiplied(by: s) 64 | let zʹ = z.multiplied(by: s) 65 | return zʹ / wʹ 66 | } 67 | // Having handled that case, we proceed pretty similarly to Priest: 68 | // 69 | // 1. Choose real scale s ~ |w|^(-¾), an exact power of the radix. 70 | // 2. wʹ ← sw 71 | // 3. zʹ ← sz 72 | // 4. return zʹ * (wʹ.conjugate / wʹ.lengthSquared) (i.e. zʹ/wʹ). 73 | // 74 | // Why is this safe and accurate? First, observe that wʹ and zʹ are both 75 | // computed exactly because: 76 | // 77 | // - s is an exact power of radix. 78 | // - wʹ ~ |w|^(¼), and hence cannot overflow or underflow. 79 | // - zʹ might overflow or underflow, but only if the final result also 80 | // overflows or underflows. (This is more subtle than I make it 81 | // sound. In particular, most of the fast ways one might try to 82 | // compute s give rise to a situation where when |w| is close to 83 | // one, multiplication by s is a dilation even though the actual 84 | // division is a contraction or vice-versa, and thus intermediate 85 | // computations might incorrectly overflow or underflow. Priest 86 | // had to take some care to avoid this situation, but we do not, 87 | // because we have already ruled out |w| ~ 1 before we call this 88 | // function.) 89 | // 90 | // Next observe that |wʹ.lengthSquared| ~ |w|^(½), so again this cannot 91 | // overflow or underflow, and neither can (wʹ.conjugate/wʹ.lengthSquared), 92 | // which has magnitude like |w|^(-¼). 93 | // 94 | // Note that because the scale factor is always a power of the radix, 95 | // the rescaling does not affect rounding, and so this algorithm is scale- 96 | // invariant compared to the mainline `/` implementation, up to the 97 | // underflow boundary. 98 | // 99 | // Note that our final assembly of the result is different from Priest; 100 | // he applies s to w twice, instead of once to w and once to z, and 101 | // does the product as (zw̅ʺ)*(1/|wʹ|²), while we do zʹ(w̅ʹ/|wʹ|²). We 102 | // prefer our version for three reasons: 103 | // 104 | // 1. it extracts a little more ILP 105 | // 2. it makes it so that we get exactly the same roundings on the 106 | // rescaled divide path as on the fast path, so that z/w = tz/tw 107 | // when tz and tw are computed exactly. 108 | // 3. it unlocks a future optimization where we hoist s and 109 | // (w̅ʹ/|wʹ|²) and make divisions all fast-path without perturbing 110 | // rounding. 111 | let s = RealType( 112 | sign: .plus, 113 | exponent: -3*w.magnitude.exponent/4, 114 | significand: 1 115 | ) 116 | let wʹ = w.multiplied(by: s) 117 | let zʹ = z.multiplied(by: s) 118 | return zʹ * wʹ.conjugate.divided(by: wʹ.lengthSquared) 119 | } 120 | 121 | /// A normalized complex number with the same phase as this value. 122 | /// 123 | /// If such a value cannot be produced (because the phase of zero and 124 | /// infinity is undefined), `nil` is returned. 125 | @inlinable 126 | public var normalized: Complex? { 127 | if length.isNormal { 128 | return self.divided(by: length) 129 | } 130 | if isZero || !isFinite { 131 | return nil 132 | } 133 | return self.divided(by: magnitude).normalized 134 | } 135 | 136 | /// The reciprocal of this value, if it can be computed without undue 137 | /// overflow or underflow. 138 | /// 139 | /// If z.reciprocal is non-nil, you can safely replace division by z with 140 | /// multiplication by this value. It is not advantageous to do this for an 141 | /// isolated division, but if you are dividing many values by a single 142 | /// denominator, this will often be a significant performance win. 143 | /// 144 | /// A typical use case looks something like this: 145 | /// ``` 146 | /// func divide(data: [Complex], by divisor: Complex) -> [Complex] { 147 | /// // If divisor is well-scaled, multiply by reciprocal. 148 | /// if let recip = divisor.reciprocal { 149 | /// return data.map { $0 * recip } 150 | /// } 151 | /// // Fallback on using division. 152 | /// return data.map { $0 / divisor } 153 | /// } 154 | /// ``` 155 | /// 156 | /// Error Bounds: 157 | /// 158 | /// Unlike real types, when working with complex types, multiplying by the 159 | /// reciprocal instead of dividing cannot change the result. If the 160 | /// reciprocal is non-nil, the two computations are always equivalent. 161 | @inlinable 162 | public var reciprocal: Complex? { 163 | let recip = 1/self 164 | if recip.isNormal || isZero || !isFinite { 165 | return recip 166 | } 167 | return nil 168 | } 169 | 170 | @_transparent 171 | public static func _relaxedAdd(_ a: Self, _ b: Self) -> Self { 172 | Complex(Relaxed.sum(a.x, b.x), Relaxed.sum(a.y, b.y)) 173 | } 174 | 175 | @_transparent 176 | public static func _relaxedMul(_ a: Self, _ b: Self) -> Self { 177 | Complex( 178 | Relaxed.sum(Relaxed.product(a.x, b.x), -Relaxed.product(a.y, b.y)), 179 | Relaxed.sum(Relaxed.product(a.x, b.y), Relaxed.product(a.y, b.x)) 180 | ) 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /Tests/RealTests/AugmentedArithmeticTests.swift: -------------------------------------------------------------------------------- 1 | //===--- AugmentedArithmeticTests.swift -----------------------*- swift -*-===// 2 | // 3 | // This source file is part of the Swift Numerics open source project 4 | // 5 | // Copyright (c) 2021 Apple Inc. and the Swift Numerics project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import RealModule 14 | import XCTest 15 | import _TestSupport 16 | 17 | final class AugmentedArithmeticTests: XCTestCase { 18 | 19 | func testTwoSumSpecials(_: T.Type) { 20 | // Must be exact and not overflow on outer bounds 21 | var x = T.greatestFiniteMagnitude 22 | var y = -T.greatestFiniteMagnitude 23 | XCTAssertEqual(Augmented.sum(x, y).head, .zero) 24 | XCTAssertEqual(Augmented.sum(x, y).tail, .zero) 25 | XCTAssert(Augmented.sum(x, y).head.isFinite) 26 | XCTAssert(Augmented.sum(x, y).tail.isFinite) 27 | // Must be exact on lower subnormal bounds 28 | x = T.leastNonzeroMagnitude 29 | y = -T.leastNonzeroMagnitude 30 | XCTAssertEqual(Augmented.sum(x, y).head, .zero) 31 | XCTAssertEqual(Augmented.sum(x, y).tail, .zero) 32 | // Must preserve floating point signs for: 33 | // (1) (+0) + (-0) == +0 34 | // (2) (-0) + (+0) == +0 35 | // (3) (-0) + (-0) == -0 36 | x = T(sign: .plus, exponent: 1, significand: 0) 37 | y = T(sign: .minus, exponent: 1, significand: 0) 38 | XCTAssertEqual(Augmented.sum(x, y).head.sign, .plus) // (1) 39 | XCTAssertEqual(Augmented.sum(x, y).tail.sign, .plus) 40 | x = T(sign: .minus, exponent: 1, significand: 0) 41 | y = T(sign: .plus, exponent: 1, significand: 0) 42 | XCTAssertEqual(Augmented.sum(x, y).head.sign, .plus) // (2) 43 | XCTAssertEqual(Augmented.sum(x, y).tail.sign, .plus) 44 | x = T(sign: .minus, exponent: 1, significand: 0) 45 | y = T(sign: .minus, exponent: 1, significand: 0) 46 | XCTAssertEqual(Augmented.sum(x, y).head.sign, .minus) // (3) 47 | XCTAssertEqual(Augmented.sum(x, y).tail.sign, .plus) 48 | // Infinity and NaN are propagated correctly 49 | XCTAssertEqual(Augmented.sum( 0, T.infinity).head, T.infinity) 50 | XCTAssertEqual(Augmented.sum( T.infinity, 0 ).head, T.infinity) 51 | XCTAssertEqual(Augmented.sum( T.infinity, T.infinity).head, T.infinity) 52 | XCTAssertEqual(Augmented.sum( 0, -T.infinity).head, -T.infinity) 53 | XCTAssertEqual(Augmented.sum(-T.infinity, 0 ).head, -T.infinity) 54 | XCTAssertEqual(Augmented.sum(-T.infinity, -T.infinity).head, -T.infinity) 55 | XCTAssert(Augmented.sum( T.infinity, -T.infinity).head.isNaN) 56 | XCTAssert(Augmented.sum(-T.infinity, T.infinity).head.isNaN) 57 | XCTAssert(Augmented.sum( T.infinity, T.nan).head.isNaN) 58 | XCTAssert(Augmented.sum( T.nan, T.infinity).head.isNaN) 59 | XCTAssert(Augmented.sum(-T.infinity, T.nan).head.isNaN) 60 | XCTAssert(Augmented.sum( T.nan, -T.infinity).head.isNaN) 61 | XCTAssert(Augmented.sum( 0, T.nan).head.isNaN) 62 | XCTAssert(Augmented.sum(T.nan, 0).head.isNaN) 63 | XCTAssert(Augmented.sum(T.nan, T.nan).head.isNaN) 64 | } 65 | 66 | func testTwoSumRandomValues(_: T.Type) { 67 | // For randomly-chosen well-scaled finite values, we expect: 68 | // (1) `head` to be exactly the IEEE 754 sum of `a + b` 69 | // (2) `tail` to be less than or equal `head.ulp/2` 70 | // (3) the result of `twoSum` for unordered input to be exactly equal to 71 | // the result of `fastTwoSum` for ordered input. 72 | var g = SystemRandomNumberGenerator() 73 | let values: [T] = (0 ..< 100).map { _ in 74 | T.random( 75 | in: T.ulpOfOne ..< 1, 76 | using: &g) 77 | } 78 | for a in values { 79 | for b in values { 80 | let twoSum = Augmented.sum(a, b) 81 | XCTAssertEqual(twoSum.head, a + b) // (1) 82 | XCTAssert(twoSum.tail.magnitude <= twoSum.head.ulp/2) // (2) 83 | let x: T = a.magnitude < b.magnitude ? b : a 84 | let y: T = a.magnitude < b.magnitude ? a : b 85 | let fastTwoSum = Augmented.sum(large: x, small: y) 86 | XCTAssertEqual(twoSum.head, fastTwoSum.head) // (3) 87 | XCTAssertEqual(twoSum.tail, fastTwoSum.tail) // (3) 88 | } 89 | } 90 | } 91 | 92 | func testTwoSumCancellation(_: T.Type) { 93 | // Must be exact for exactly representable values 94 | XCTAssertEqual(Augmented.sum( 0.984375, 1.375).head, 2.359375) 95 | XCTAssertEqual(Augmented.sum( 0.984375, 1.375).tail, 0.0) 96 | XCTAssertEqual(Augmented.sum(-0.984375, 1.375).head, 0.390625) 97 | XCTAssertEqual(Augmented.sum(-0.984375, 1.375).tail, 0.0) 98 | XCTAssertEqual(Augmented.sum( 0.984375,-1.375).head,-0.390625) 99 | XCTAssertEqual(Augmented.sum( 0.984375,-1.375).tail, 0.0) 100 | XCTAssertEqual(Augmented.sum(-0.984375,-1.375).head,-2.359375) 101 | XCTAssertEqual(Augmented.sum(-0.984375,-1.375).tail, 0.0) 102 | XCTAssertEqual(Augmented.sum( 1.375, 0.984375).head, 2.359375) 103 | XCTAssertEqual(Augmented.sum( 1.375, 0.984375).tail, 0.0) 104 | XCTAssertEqual(Augmented.sum( 1.375,-0.984375).head, 0.390625) 105 | XCTAssertEqual(Augmented.sum( 1.375,-0.984375).tail, 0.0) 106 | XCTAssertEqual(Augmented.sum(-1.375, 0.984375).head,-0.390625) 107 | XCTAssertEqual(Augmented.sum(-1.375, 0.984375).tail, 0.0) 108 | XCTAssertEqual(Augmented.sum(-1.375,-0.984375).head,-2.359375) 109 | XCTAssertEqual(Augmented.sum(-1.375,-0.984375).tail, 0.0) 110 | // Must handle cancellation when `b` is not representable in `a` and 111 | // we expect `b` to be lost entirely in the calculation of `a + b`. 112 | var a: T = 1.0 113 | var b: T = .ulpOfOne * .ulpOfOne 114 | var twoSum = Augmented.sum(a, b) 115 | XCTAssertEqual(twoSum.head, a) // a + b = a 116 | XCTAssertEqual(twoSum.tail, b) // Error: b 117 | twoSum = Augmented.sum( a, -b) 118 | XCTAssertEqual(twoSum.head, a) 119 | XCTAssertEqual(twoSum.tail,-b) 120 | twoSum = Augmented.sum(-a, b) 121 | XCTAssertEqual(twoSum.head,-a) 122 | XCTAssertEqual(twoSum.tail, b) 123 | twoSum = Augmented.sum(-a, -b) 124 | XCTAssertEqual(twoSum.head,-a) 125 | XCTAssertEqual(twoSum.tail,-b) 126 | // Must handle cancellation when `b` is only partially representable in `a`. 127 | // We expect the fractional digits of `b` to be cancelled in the following 128 | // example but the fractional digits to be preserved in `tail`. 129 | let exponent = T.Exponent(T.significandBitCount + 1) 130 | a = T(sign: .plus, exponent: exponent, significand: 1.0) 131 | b = 256 + 0.5 132 | twoSum = Augmented.sum( a, b) 133 | XCTAssertEqual(twoSum.head, a + 256) 134 | XCTAssertEqual(twoSum.tail, 0.5) 135 | twoSum = Augmented.sum( a, -b) 136 | XCTAssertEqual(twoSum.head, a - 256) 137 | XCTAssertEqual(twoSum.tail, -0.5) 138 | twoSum = Augmented.sum(-a, b) 139 | XCTAssertEqual(twoSum.head, -a + 256) 140 | XCTAssertEqual(twoSum.tail, 0.5) 141 | twoSum = Augmented.sum(-a, -b) 142 | XCTAssertEqual(twoSum.head, -a - 256) 143 | XCTAssertEqual(twoSum.tail, -0.5) 144 | } 145 | 146 | func testTwoSum() { 147 | testTwoSumSpecials(Float32.self) 148 | testTwoSumRandomValues(Float32.self) 149 | testTwoSumCancellation(Float32.self) 150 | testTwoSumSpecials(Float64.self) 151 | testTwoSumRandomValues(Float64.self) 152 | testTwoSumCancellation(Float64.self) 153 | #if (arch(i386) || arch(x86_64)) && !os(Windows) && !os(Android) 154 | testTwoSumSpecials(Float80.self) 155 | testTwoSumRandomValues(Float80.self) 156 | testTwoSumCancellation(Float80.self) 157 | #endif 158 | } 159 | } 160 | 161 | --------------------------------------------------------------------------------