├── .codecov.yml ├── REQUIRE ├── .gitignore ├── .travis.yml ├── Project.toml ├── LICENSE ├── appveyor.yml ├── old_logic_table.md ├── README.md ├── test └── runtests.jl └── src └── FastRounding.jl /.codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | -------------------------------------------------------------------------------- /REQUIRE: -------------------------------------------------------------------------------- 1 | julia 0.7 2 | ErrorfreeArithmetic 0.1.3 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.jl.cov 2 | *.jl.*.cov 3 | *.jl.mem 4 | deps/deps.jl 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Documentation: http://docs.travis-ci.com/user/languages/julia/ 2 | language: julia 3 | os: 4 | - linux 5 | - osx 6 | julia: 7 | - 0.7 8 | - 1.0 9 | - nightly 10 | notifications: 11 | email: false 12 | after_success: 13 | - julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())'; 14 | -------------------------------------------------------------------------------- /Project.toml: -------------------------------------------------------------------------------- 1 | desc = "Rounding without rounding modes, using error-free transformations." 2 | keywords = ["round", "directed rounding", "error-free transformations", "floating-point"] 3 | name = "FastRounding" 4 | author = "Jeffrey Sarnoff" 5 | uuid = "fa42c844-2597-5d31-933b-ebd51ab2693f" 6 | version = "v0.2.0" 7 | 8 | [deps] 9 | LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" 10 | ErrorfreeArithmetic = "90fa49ef-747e-5e6f-a989-263ba693cf1a" 11 | 12 | [compat] 13 | ErrorfreeArithmetic = ">=0.1.3" 14 | 15 | [extras] 16 | Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" 17 | 18 | [targets] 19 | test = ["Test"] 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jeffrey Sarnoff 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - julia_version: 0.7 4 | - julia_version: 1 5 | - julia_version: nightly 6 | 7 | platform: 8 | - x86 # 32-bit 9 | - x64 # 64-bit 10 | 11 | # # Uncomment the following lines to allow failures on nightly julia 12 | # # (tests will run but not make your overall status red) 13 | # matrix: 14 | # allow_failures: 15 | # - julia_version: latest 16 | 17 | branches: 18 | only: 19 | - master 20 | - /release-.*/ 21 | 22 | notifications: 23 | - provider: Email 24 | on_build_success: false 25 | on_build_failure: false 26 | on_build_status_changed: false 27 | 28 | install: 29 | - ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1")) 30 | 31 | build_script: 32 | - echo "%JL_BUILD_SCRIPT%" 33 | - C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%" 34 | 35 | test_script: 36 | - echo "%JL_TEST_SCRIPT%" 37 | - C:\julia\bin\julia -e "%JL_TEST_SCRIPT%" 38 | 39 | # # Uncomment to support code coverage upload. Should only be enabled for packages 40 | # # which would have coverage gaps without running on Windows 41 | # on_success: 42 | # - echo "%JL_CODECOV_SCRIPT%" 43 | # - C:\julia\bin\julia -e "%JL_CODECOV_SCRIPT%" 44 | -------------------------------------------------------------------------------- /old_logic_table.md: -------------------------------------------------------------------------------- 1 | 2 | ## Internal Logic 3 | 4 | #### RoundNearest 5 | 6 | | sign of `hi` | sign of `lo` | rounding | fastrounding | 7 | |:--:|:--:|:--:|:--:| 8 | | + | + | `hi` | `hi` | 9 | | + | - | `hi` | `hi` | 10 | | - | + | `hi` | `hi` | 11 | | - | - | `hi` | `hi` | 12 | 13 | #### RoundDown 14 | 15 | | sign of `hi` | sign of `lo` | rounding | fastrounding | 16 | |:--:|:--:|:--:|:--:| 17 | | + | + | `hi` | `hi` | 18 | | + | - | `prevfloat(hi)` | `next_nearerto_zero(hi) == prev_float(hi)` | 19 | | - | + | `hi` | `hi` | 20 | | - | - | `prevfloat(hi)` | `next_awayfrom_zero(hi) == prev_float(hi)` | 21 | 22 | #### RoundUp 23 | 24 | | sign of `hi` | sign of `lo` | rounding | fastrounding | 25 | |:--:|:--:|:--:|:--:| 26 | | + | + | `nextfloat(hi)` | `next_awayfrom_zero(hi) == next_float(hi)` | 27 | | + | - | `hi` | `hi` | 28 | | - | + | `nextfloat(hi)` | `next_nearerto_zero(hi) == next_float(hi)` | 29 | | - | - | `hi` | `hi` | 30 | 31 | #### RoundFromZero 32 | 33 | | sign of `hi` | sign of `lo` | rounding | fastrounding | 34 | |:--:|:--:|:--:|:--:| 35 | | + | + | `nextfloat(hi)` | `next_awayfrom_zero(hi)` | 36 | | + | - | `hi` | `hi` | 37 | | - | + | `hi` | `hi` | 38 | | - | - | `prevfloat(hi)` | `next_awayfrom_zero(hi)` | 39 | 40 | 41 | #### RoundToZero 42 | 43 | | sign of `hi` | sign of `lo` | rounding | fastrounding | 44 | |:--:|:--:|:--:|:--:| 45 | | + | + | `hi` | `hi` | 46 | | + | - | `prevfloat(hi)` | `next_nearerto_zero(hi)` | 47 | | - | + | `nextfloat(hi)` | `next_nearerto_zero(hi)` | 48 | | - | - | `hi` | `hi` | 49 | 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FastRounding.jl 2 | ### Faster directed rounding for inline arithmetic 3 | 4 | 5 | #### Copyright © 2015-2018 by Jeffrey Sarnoff. Released under the MIT License. 6 | 7 | ----- 8 | 9 | [![Build Status](https://travis-ci.org/JeffreySarnoff/FastRounding.jl.svg?branch=master)](https://travis-ci.org/JeffreySarnoff/FastRounding.jl) 10 | 11 | ----- 12 | 13 | 14 | ## Quick Overview 15 | 16 | * This package provides arithmetic with directed rounding 17 | 18 | * The numeric types 19 | { Float32, Float64 } 20 | 21 | * The arithmetic operations 22 | { +, -, *, inv, /, sqrt } 23 | 24 | * The directed rounding modes 25 | { RoundNearest, RoundUp, RoundDown, RoundToZero, RoundFromZero } 26 | 27 | * These functions ran faster than Julia's erstwhile `setrounding` 28 | 29 | ------- 30 | 31 | ### Rounding Modes 32 | 33 | - RoundNearest 34 | - RoundUp 35 | - RoundDown 36 | - RoundToZero 37 | - RoundFromZero 38 | 39 | ### Exports 40 | 41 | - add_round, sub_round 42 | - square_round, mul_round 43 | - sqrt_round, inv_round 44 | - div_round 45 | 46 | ### Equivalent Exports 47 | 48 | | subscript | signifies mode | 49 | |:---------:|:--------------| 50 | | ◌₌ | RoundNearest | 51 | | ◌₊ | RoundUp | 52 | | ◌₋ | RoundDown | 53 | | ◌₀ | RoundToZero | 54 | | ◌₁ | RoundFromZero | 55 | 56 | ----- 57 | 58 | | unicode op | arithmetic op | 59 | |:----------:|:-------------:| 60 | | ⊕ | + | 61 | | ⊖ | - | 62 | | ⊗ | * | 63 | | ⚆ | inv | 64 | | ⊘ | / | 65 | | ⊚ | square | 66 | | ⊙ | sqrt | 67 | 68 | 69 | ## Use 70 | 71 | ```julia 72 | julia> using FastRounding 73 | 74 | julia> a = Float32(pi) 75 | 3.1415927f0 76 | 77 | julia> b = inv(Float32(pi)) 78 | 0.31830987f0 79 | 80 | julia> mul_round(a, b, RoundUp) 81 | 1.0f0 82 | 83 | julia> mul_round(a, b, RoundNearest) 84 | 1.0f0 85 | 86 | julia> mul_round(a, b, RoundDown) 87 | 0.99999994f0 88 | 89 | julia> mul_round(a, b, RoundToZero) 90 | 0.99999994f0 91 | 92 | julia> mul_round(a, b, RoundFromZero) 93 | 1.0f0 94 | 95 | julia> two = Float64(2) 96 | 2.0 97 | 98 | julia> sqrt_round(two, RoundUp) 99 | 1.4142135623730951 100 | 101 | julia> sqrt_round(two, RoundNearest) 102 | 1.4142135623730951 103 | 104 | julia> sqrt_round(two, RoundDown) 105 | 1.414213562373095 106 | 107 | julia> inv_round(-two, RoundToZero) 108 | -0.5 109 | 110 | julia> inv_round(-two, RoundFromZero) 111 | -0.5000000000000001 112 | 113 | julia> ⊙₋(two) # sqrt(2) rounding down 114 | 1.414213562373095 115 | 116 | julia> two ⊘₁ ans # 2 / (sqrt(2) rounding down) rounding away from zero 117 | 1.4142135623730954 118 | 119 | ``` 120 | 121 | ------- 122 | 123 | ## Conformance 124 | 125 | These functions conform to the requirements of the IEEE Interval Floating Point Standard 126 | for directed rounding, passing their tests as implemented in IntervalArithmetic.jl. 127 | 128 | While not required by the IEEE Floating Point Standard, RoundToZero and RoundFromZero modes exist. 129 | 130 | The determination of last bit(s) is correct to twice the significance of the rounded value. 131 | We do not provide the equivalent of infinitly precise evaluation when at doubled the given 132 | precision, all least significant bits are zero. We are accurate and fast. 133 | 134 | If you require that RoundUp, RoundDown pairs assure enclosure of the theoretical result 135 | at a precision that exceeds 106 bits when working with Float64s (which have 53 significant bits), 136 | please let me know. I could force `nextfloat` and `prevfloat` calls in those cases, forgoing 137 | the tightest results for the most inclusive. Those routines run a little slower than these. 138 | Note that those routines may not match the Standard test suite values in those adjusted cases. 139 | 140 | -------------------------------------------------------------------------------- /test/runtests.jl: -------------------------------------------------------------------------------- 1 | using FastRounding 2 | 3 | using Test 4 | 5 | const SysFloat = Union{Float64, Float32} 6 | 7 | a = 4/3 8 | b = 1/7 9 | c = -b 10 | 11 | 12 | function testrounding(op::Function, a::T, b::T, mode::RoundingMode) where T<: SysFloat 13 | S = typeof(op(a,b)) 14 | 15 | c = setrounding(BigFloat, mode) do 16 | S(op(big(a), big(b))) 17 | end 18 | return c 19 | end 20 | 21 | @testset "Test Rounding" begin 22 | @testset "a + b" begin 23 | @test testrounding(+, a, b, RoundUp) == add_round(a, b, RoundUp) 24 | @test testrounding(+, a, b, RoundDown) == add_round(a, b, RoundDown) 25 | @test testrounding(+, a, b, RoundToZero) == add_round(a, b, RoundToZero) 26 | @test testrounding(+, a, b, RoundNearest) == add_round(a, b, RoundNearest) 27 | end 28 | @testset "a - b" begin 29 | @test testrounding(-, a, b, RoundUp) == sub_round(a, b, RoundUp) 30 | @test testrounding(-, a, b, RoundDown) == sub_round(a, b, RoundDown) 31 | @test testrounding(-, a, b, RoundToZero) == sub_round(a, b, RoundToZero) 32 | @test testrounding(-, a, b, RoundNearest) == sub_round(a, b, RoundNearest) 33 | end 34 | @testset "a * b" begin 35 | @test testrounding(*, a, b, RoundUp) == mul_round(a, b, RoundUp) 36 | @test testrounding(*, a, b, RoundDown) == mul_round(a, b, RoundDown) 37 | @test testrounding(*, a, b, RoundToZero) == mul_round(a, b, RoundToZero) 38 | @test testrounding(*, a, b, RoundNearest) == mul_round(a, b, RoundNearest) 39 | end 40 | @testset "a / b" begin 41 | @test testrounding(/, a, b, RoundUp) == div_round(a, b, RoundUp) 42 | @test testrounding(/, a, b, RoundDown) == div_round(a, b, RoundDown) 43 | @test testrounding(/, a, b, RoundToZero) == div_round(a, b, RoundToZero) 44 | @test testrounding(/, a, b, RoundNearest) == div_round(a, b, RoundNearest) 45 | end 46 | @testset "a + c" begin 47 | @test testrounding(+, a, c, RoundUp) == add_round(a, c, RoundUp) 48 | @test testrounding(+, a, c, RoundDown) == add_round(a, c, RoundDown) 49 | @test testrounding(+, a, c, RoundToZero) == add_round(a, c, RoundToZero) 50 | @test testrounding(+, a, c, RoundNearest) == add_round(a, c, RoundNearest) 51 | end 52 | @testset "a - c" begin 53 | @test testrounding(-, a, c, RoundUp) == sub_round(a, c, RoundUp) 54 | @test testrounding(-, a, c, RoundDown) == sub_round(a, c, RoundDown) 55 | @test testrounding(-, a, c, RoundToZero) == sub_round(a, c, RoundToZero) 56 | @test testrounding(-, a, c, RoundNearest) == sub_round(a, c, RoundNearest) 57 | end 58 | @testset "a * c" begin 59 | @test testrounding(*, a, c, RoundUp) == mul_round(a, c, RoundUp) 60 | @test testrounding(*, a, c, RoundDown) == mul_round(a, c, RoundDown) 61 | @test testrounding(*, a, c, RoundToZero) == mul_round(a, c, RoundToZero) 62 | @test testrounding(*, a, c, RoundNearest) == mul_round(a, c, RoundNearest) 63 | end 64 | @testset "a / c" begin 65 | @test testrounding(/, a, c, RoundUp) == div_round(a, c, RoundUp) 66 | @test testrounding(/, a, c, RoundDown) == div_round(a, c, RoundDown) 67 | @test testrounding(/, a, c, RoundToZero) == div_round(a, c, RoundToZero) 68 | @test testrounding(/, a, c, RoundNearest) == div_round(a, c, RoundNearest) 69 | end 70 | @testset "1.0 + 0.0" begin 71 | @test testrounding(+, 1.0, 0.0, RoundUp) == 1.0 72 | @test testrounding(+, 1.0, 0.0, RoundDown) == 1.0 73 | @test testrounding(+, 1.0, 0.0, RoundToZero) == 1.0 74 | @test testrounding(+, 1.0, 0.0, RoundNearest) == 1.0 75 | end 76 | @testset "1.0 - 0.0" begin 77 | @test testrounding(-, 1.0, 0.0, RoundUp) == 1.0 78 | @test testrounding(-, 1.0, 0.0, RoundDown) == 1.0 79 | @test testrounding(-, 1.0, 0.0, RoundToZero) == 1.0 80 | @test testrounding(-, 1.0, 0.0, RoundNearest) == 1.0 81 | end 82 | @testset "1.0 * 1.0" begin 83 | @test testrounding(*, 1.0, 1.0, RoundUp) == 1.0 84 | @test testrounding(*, 1.0, 1.0, RoundDown) == 1.0 85 | @test testrounding(*, 1.0, 1.0, RoundToZero) == 1.0 86 | @test testrounding(*, 1.0, 1.0, RoundNearest) == 1.0 87 | end 88 | @testset "1.0 / 1.0" begin 89 | @test testrounding(/, 1.0, 1.0, RoundUp) == 1.0 90 | @test testrounding(/, 1.0, 1.0, RoundDown) == 1.0 91 | @test testrounding(/, 1.0, 1.0, RoundToZero) == 1.0 92 | @test testrounding(/, 1.0, 1.0, RoundNearest) == 1.0 93 | end 94 | end 95 | 96 | -------------------------------------------------------------------------------- /src/FastRounding.jl: -------------------------------------------------------------------------------- 1 | module FastRounding 2 | 3 | export add_round, sub_round, mul_round, square_round, 4 | inv_round, div_round, sqrt_round, 5 | ⊕₊, ⊕₋, ⊕₌, ⊕₀, ⊕₁, 6 | ⊖₊, ⊖₋, ⊖₌, ⊖₀, ⊖₁, 7 | ⊗₊, ⊗₋, ⊗₌, ⊗₀, ⊗₁, 8 | ⊘₊, ⊘₋, ⊘₌, ⊘₀, ⊘₁, 9 | ⊚₊, ⊚₋, ⊚₌, ⊚₀, ⊚₁, 10 | ⊙₊, ⊙₋, ⊙₌, ⊙₀, ⊙₁, 11 | ⚆₊, ⚆₋, ⚆₌, ⚆₀, ⚆₁ 12 | 13 | 14 | using ErrorfreeArithmetic 15 | 16 | const SysFloat = Union{Float64, Float32} # fast iff fma is available in hardware 17 | 18 | 19 | @inline function add_round(a::T, b::T, rounding::R)::T where {T<:SysFloat, R<:RoundingMode} 20 | hi, lo = two_sum(a, b) 21 | return round_errorfree(hi, lo, rounding) 22 | end 23 | add_round(a::T, b::T) where {T<:SysFloat} = a + b 24 | 25 | ⊕₌(a::T, b::T) where {T<:SysFloat} = add_round(a, b, RoundNearest) 26 | ⊕₊(a::T, b::T) where {T<:SysFloat} = add_round(a, b, RoundUp) 27 | ⊕₋(a::T, b::T) where {T<:SysFloat} = add_round(a, b, RoundDown) 28 | ⊕₀(a::T, b::T) where {T<:SysFloat} = add_round(a, b, RoundToZero) 29 | ⊕₁(a::T, b::T) where {T<:SysFloat} = add_round(a, b, RoundFromZero) 30 | 31 | @inline function sub_round(a::T, b::T, rounding::R)::T where {T<:SysFloat, R<:RoundingMode} 32 | hi, lo = two_diff(a, b) 33 | return round_errorfree(hi, lo, rounding) 34 | end 35 | sub_round(a::T, b::T) where {T<:SysFloat} = a - b 36 | 37 | ⊖₌(a::T, b::T) where {T<:SysFloat} = sub_round(a, b, RoundNearest) 38 | ⊖₊(a::T, b::T) where {T<:SysFloat} = sub_round(a, b, RoundUp) 39 | ⊖₋(a::T, b::T) where {T<:SysFloat} = sub_round(a, b, RoundDown) 40 | ⊖₀(a::T, b::T) where {T<:SysFloat} = sub_round(a, b, RoundToZero) 41 | ⊖₁(a::T, b::T) where {T<:SysFloat} = sub_round(a, b, RoundFromZero) 42 | 43 | @inline function mul_round(a::T, b::T, rounding::R)::T where {T<:SysFloat, R<:RoundingMode} 44 | hi, lo = two_prod(a, b) 45 | return round_errorfree(hi, lo, rounding) 46 | end 47 | mul_round(a::T, b::T) where {T<:SysFloat} = a * b 48 | 49 | ⊗₌(a::T, b::T) where {T<:SysFloat} = mul_round(a, b, RoundNearest) 50 | ⊗₊(a::T, b::T) where {T<:SysFloat} = mul_round(a, b, RoundUp) 51 | ⊗₋(a::T, b::T) where {T<:SysFloat} = mul_round(a, b, RoundDown) 52 | ⊗₀(a::T, b::T) where {T<:SysFloat} = mul_round(a, b, RoundToZero) 53 | ⊗₁(a::T, b::T) where {T<:SysFloat} = inv_round(a, b, RoundFromZero) 54 | 55 | @inline function inv_round(a::T, rounding::R)::T where {T<:SysFloat, R<:RoundingMode} 56 | hi, lo = two_inv(a) 57 | return round_errorfree(hi, lo, rounding) 58 | end 59 | inv_round(a::T) where {T<:SysFloat} = inv(a) 60 | 61 | ⚆₌(a::T) where {T<:SysFloat} = inv_round(a, RoundNearest) 62 | ⚆₊(a::T) where {T<:SysFloat} = inv_round(a, RoundUp) 63 | ⚆₋(a::T) where {T<:SysFloat} = inv_round(a, RoundDown) 64 | ⚆₀(a::T) where {T<:SysFloat} = inv_round(a, RoundToZero) 65 | ⚆₁(a::T) where {T<:SysFloat} = inv_round(a, RoundFromZero) 66 | 67 | @inline function div_round(a::T, b::T, rounding::R)::T where {T<:SysFloat, R<:RoundingMode} 68 | hi, lo = two_div(a, b) 69 | return round_errorfree(hi, lo, rounding) 70 | end 71 | div_round(a::T, b::T) where {T<:SysFloat} = a / b 72 | 73 | ⊘₌(a::T, b::T) where {T<:SysFloat} = div_round(a, b, RoundNearest) 74 | ⊘₊(a::T, b::T) where {T<:SysFloat} = div_round(a, b, RoundUp) 75 | ⊘₋(a::T, b::T) where {T<:SysFloat} = div_round(a, b, RoundDown) 76 | ⊘₀(a::T, b::T) where {T<:SysFloat} = div_round(a, b, RoundToZero) 77 | ⊘₁(a::T, b::T) where {T<:SysFloat} = div_round(a, b, RoundFromZero) 78 | 79 | @inline function square_round(a::T, rounding::R)::T where {T<:SysFloat, R<:RoundingMode} 80 | hi, lo = two_square(a) 81 | return round_errorfree(hi, lo, rounding) 82 | end 83 | square_round(a::T) where {T<:SysFloat} = a^2 84 | 85 | ⊚₌(a::T) where {T<:SysFloat} = square_round(a, b, RoundNearest) 86 | ⊚₊(a::T) where {T<:SysFloat} = square_round(a, b, RoundUp) 87 | ⊚₋(a::T) where {T<:SysFloat} = square_round(a, b, RoundDown) 88 | ⊚₀(a::T) where {T<:SysFloat} = square_round(a, b, RoundToZero) 89 | ⊚₁(a::T) where {T<:SysFloat} = square_round(a, b, RoundFromZero) 90 | 91 | @inline function sqrt_round(a::T, rounding::R)::T where {T<:SysFloat, R<:RoundingMode} 92 | hi, lo = two_sqrt(a) 93 | return round_errorfree(hi, lo, rounding) 94 | end 95 | sqrt_round(a::T) where {T<:SysFloat} = sqrt(a) 96 | 97 | ⊙₌(a::T) where {T<:SysFloat} = sqrt_round(a, RoundNearest) 98 | ⊙₊(a::T) where {T<:SysFloat} = sqrt_round(a, RoundUp) 99 | ⊙₋(a::T) where {T<:SysFloat} = sqrt_round(a, RoundDown) 100 | ⊙₀(a::T) where {T<:SysFloat} = sqrt_round(a, RoundToZero) 101 | ⊙₁(a::T) where {T<:SysFloat} = sqrt_round(a, RoundFromZero) 102 | 103 | 104 | #= 105 | To perform arithmetic with directed rounding more rapidly 106 | we use error-free transformations to control rounding. 107 | =# 108 | 109 | @inline function round_errorfree(hi::T, lo::T, ::RoundingMode{:Nearest})::T where {T<:SysFloat} 110 | !isinf(hi) && return hi 111 | return signbit(hi) ? T(-Inf) : T(Inf) 112 | end 113 | 114 | @inline function round_errorfree(hi::T, lo::T, ::RoundingMode{:ToZero})::T where {T<:SysFloat} 115 | !isinf(hi) && return signbit(hi) != signbit(lo) ? next_nearerto_zero(hi) : hi 116 | return signbit(hi) ? nextfloat(T(-Inf)) : prevfloat(T(Inf)) 117 | end 118 | 119 | @inline function round_errorfree(hi::T, lo::T, ::RoundingMode{:FromZero})::T where {T<:SysFloat} 120 | !isinf(hi) && return signbit(hi) == signbit(lo) ? next_awayfrom_zero(hi) : hi 121 | return signbit(hi) ? T(-Inf) : T(Inf) 122 | end 123 | 124 | @inline function round_errorfree(hi::T, lo::T, ::RoundingMode{:Up})::T where {T<:SysFloat} 125 | !isinf(hi) && return (lo<=zero(T) || isnan(lo)) ? hi : nextfloat(hi) 126 | return signbit(hi) ? nextfloat(T(-Inf)) : T(Inf) 127 | end 128 | 129 | @inline function round_errorfree(hi::T, lo::T, ::RoundingMode{:Down})::T where {T<:SysFloat} 130 | !isinf(hi) && return (lo>=zero(T) || isnan(lo)) ? hi : prevfloat(hi) 131 | return signbit(hi) ? T(-Inf) : prevfloat(T(Inf)) 132 | end 133 | 134 | @inline next_nearerto_zero(x::T) where {T<:SysFloat} = !signbit(x) ? prevfloat(x) : nextfloat(x) 135 | @inline next_awayfrom_zero(x::T) where {T<:SysFloat} = !signbit(x) ? nextfloat(x) : prevfloat(x) 136 | 137 | end # module 138 | --------------------------------------------------------------------------------