├── REQUIRE ├── .codecov.yml ├── .gitignore ├── .travis.yml ├── test └── runtests.jl ├── LICENSE ├── appveyor.yml ├── README.md └── src └── ErrorfreeArithmetic.jl /REQUIRE: -------------------------------------------------------------------------------- 1 | julia 0.5.0 2 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | -------------------------------------------------------------------------------- /.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.5.0 8 | - nightly 9 | notifications: 10 | email: false 11 | after_success: 12 | - julia -e 'cd(Pkg.dir("ErrorfreeArithmetic")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())'; 13 | -------------------------------------------------------------------------------- /test/runtests.jl: -------------------------------------------------------------------------------- 1 | using ErrorfreeArithmetic 2 | using Base.Test 3 | 4 | function correct_add{T<:AbstractFloat}(a::T, b::T) 5 | aa = convert(BigFloat, a) 6 | bb = convert(BigFloat, b) 7 | ab = aa + bb 8 | hi = T(ab) 9 | lo = T(ab - hi) 10 | return hi, lo 11 | end 12 | 13 | function correct_multiply{T<:AbstractFloat}(a::T, b::T) 14 | aa = convert(BigFloat, a) 15 | bb = convert(BigFloat, b) 16 | ab = aa * bb 17 | hi = T(ab) 18 | lo = T(ab - hi) 19 | return hi, lo 20 | end 21 | 22 | @test add_errorfree(sqrt(pi), sqrt(catalan)) == correct_add(sqrt(pi), sqrt(catalan)) 23 | @test multiply_errorfree(sqrt(pi), sqrt(catalan)) == correct_multiply(sqrt(pi), sqrt(catalan)) 24 | 25 | @test add_errorfree(1/sqrt(pi), 1/sqrt(catalan)) == correct_add(1/sqrt(pi), 1/sqrt(catalan)) 26 | @test multiply_errorfree(1/sqrt(pi), 1/sqrt(catalan)) == correct_multiply(1/sqrt(pi), 1/sqrt(catalan)) 27 | 28 | @test multiply_errorfree(Inf, 3.1) == (Inf, 0.0) 29 | -------------------------------------------------------------------------------- /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 | language: julia 2 | os: 3 | - linux 4 | - osx 5 | julia: 6 | - 0.5.0 7 | - nightly 8 | environment: 9 | matrix: 10 | - JULIAVERSION: "julialang/bin/winnt/x86/0.5/julia-0.5-latest-win32.exe" 11 | - JULIAVERSION: "julialang/bin/winnt/x64/0.5/julia-0.5-latest-win64.exe" 12 | - JULIAVERSION: "julianightlies/bin/winnt/x86/julia-latest-win32.exe" 13 | - JULIAVERSION: "julianightlies/bin/winnt/x64/julia-latest-win64.exe" 14 | matrix: 15 | allow_failures: 16 | - julia: nightly 17 | - JULIAVERSION: "julianightlies/bin/winnt/x86/julia-latest-win32.exe" 18 | - JULIAVERSION: "julianightlies/bin/winnt/x64/julia-latest-win64.exe" 19 | branches: 20 | only: 21 | - master 22 | - /release-.*/ 23 | notifications: 24 | - provider: Email 25 | on_build_success: false 26 | on_build_failure: false 27 | on_build_status_changed: false 28 | script: 29 | # - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi 30 | - julia -e 'Pkg.init(); Pkg.clone("https://github.com/JeffreySarnoff/ErrorfreeArithmetic.jl")' 31 | - julia -e 'Pkg.test("ErrorfreeArithmetic",coverage=true)' 32 | after_success: 33 | - julia -e 'Pkg.clone("https://github.com/MichaelHatherly/Documenter.jl")' 34 | - julia -e 'cd(Pkg.dir("ErrorfreeArithmetic")); include(joinpath("docs", "make.jl"))' 35 | - julia -e 'cd(Pkg.dir("ErrorfreeArithmetic")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())' 36 | - julia -e 'cd(Pkg.dir("ErrorfreeArithmetic")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(process_folder())' 37 | install: 38 | # Download most recent Julia Windows binary 39 | - ps: (new-object net.webclient).DownloadFile( 40 | $("http://s3.amazonaws.com/"+$env:JULIAVERSION), 41 | "C:\projects\julia-binary.exe") 42 | # Run installer silently, output to C:\projects\julia 43 | - C:\projects\julia-binary.exe /S /D=C:\projects\julia 44 | 45 | build_script: 46 | # Need to convert from shallow to complete for Pkg.clone to work 47 | - IF EXIST .git\shallow (git fetch --unshallow) 48 | - C:\projects\julia\bin\julia -e "versioninfo(); 49 | Pkg.clone(pwd(), \"ErrorfreeArithmetic\"); Pkg.build(\"ErrorfreeArithmetic\")" 50 | 51 | test_script: 52 | - C:\projects\julia\bin\julia -e "Pkg.test(\"ErrorfreeArithmetic\")" 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ErrorfreeArithmetic.jl 2 | Error-free transformations are used to get results with extra accuracy. 3 | 4 | 5 | #### Copyright © 2016-2017 by Jeffrey Sarnoff. Released under the MIT License. 6 | 7 | ----- 8 | 9 | [![Build Status](https://travis-ci.org/JeffreySarnoff/ErrorfreeArithmetic.jl.svg?branch=master)](https://travis-ci.org/JeffreySarnoff/ErrorfreeArithmetic.jl) 10 | 11 | ----- 12 | 13 | ## exports 14 | 15 | *These are error-free transformations.* 16 | 17 | add_errorfree, subtract_errorfree, 18 | add_inorder_errorfree, subtract_inorder_errorfree, 19 | square_errorfree, multiply_errorfree, 20 | fma_errorfree, fms_errorfree 21 | 22 | *This is likely an error-free transformation, though I have not seen a proof.* 23 | inv_errorfree 24 | 25 | *While these are not strictly error-free transformations, they are very nearly so.* 26 | *They are as accurate as possible given the working precision.* 27 | 28 | cube_accurately, divide_accurately, 29 | sqrt_accurately, invsqrt_accurately 30 | 31 | ## introduction 32 | 33 | Error-free transformations return a tuple of the nominal result and the residual from the result (the left-over part). 34 | 35 | Error-free addition sums two floating point values (a, b) and returns two floating point values (hi, lo) such that: 36 | * `(+)(a, b) == hi` 37 | * `|hi| > |lo|` and `(+)(hi, lo) == hi` *abs(hi) and abs(lo) do not share significant bits* 38 | 39 | Here is how it is done: 40 | 41 | ```julia 42 | function add_errorfree(a::T, b::T) where T<:Union{Float64, Float32} 43 | a_plus_b_hipart = a + b 44 | b_asthe_summand = a_plus_b_hipart - a 45 | a_plus_b_lopart = (a - (a_plus_b_hipart - b_asthe_summand)) + (b - b_asthe_summand) 46 | return a_plus_b_hipart, a_plus_b_lopart 47 | end 48 | 49 | a = Float32(1/golden^2) # 0.3819_6602f0 50 | b = Float32(pi^3) # 31.0062_7700f0 51 | a_plus_b = a + b # 31.3882_4300f0 52 | 53 | hi, lo = add_errorfree(a,b) # (31.3882_4300f0, 3.8743_0270f-7) 54 | 55 | a_plus_b == hi # true 56 | abs(hi) > abs(lo) && hi + lo == hi # true 57 | 58 | ``` 59 | The `lo` part is a portion of the accurate value, it is (most of) the residuum that the `hi` part could not represent. 60 | The `hi` part runs out of significant bits before the all of the accurate value is represented. We can see this: 61 | ```julia 62 | a = Float32(1/golden^2) # 0.3819_6602f0 63 | b = Float32(pi^3) # 31.0062_7700f0 64 | 65 | hi, lo = add_errorfree(a,b) # (31.3882_4300f0, 3.8743_0270f-7) 66 | 67 | a_plus_b_accurate = BigFloat(a) + BigFloat(b) 68 | lo_accurate = Float32(a_plus_b_accurate - hi) 69 | 70 | lo == lo_accurate # true 71 | ``` 72 | 73 | ## use 74 | 75 | This package is intended to be used in the support of other work. 76 | All routines expect Float64 or Float32 or Float16 values. 77 | 78 | ## references 79 | 80 | Takeshi Ogita, Siegfried M. Rump, and Shin'ichi Oishi 81 | Accurate Sum and Dot Product 82 | SIAM J. Sci. Comput., 26(6), 1955–1988. 83 | Published online: 25 July 2006 84 | [DOI: 10.1137/030601818](http://dx.doi.org/10.1137/030601818) 85 | 86 | Stef Graillat, Valérie Ménissier-Morain 87 | Error-Free Transformations in Real and Complex Floating Point Arithmetic 88 | International Symposium on Nonlinear Theory and its Applications (NOLTA'07), Sep 2007, Vancouver, Canada. 89 | Proceedings of International Symposium on Nonlinear Theory and its Applications (NOLTA'07), pp.341-344. 90 | https://hal.archives-ouvertes.fr/hal-01306229 91 | 92 | Sylvie Boldo, Stef Graillat, and Jean-Michel Muller 93 | On the robustness of the 2Sum and Fast2Sum algorithms 94 | ACM Transactions on Mathematical Software, Association for Computing Machinery, 2017 95 | https://hal.inria.fr/ensl-01310023 96 | 97 | -------------------------------------------------------------------------------- /src/ErrorfreeArithmetic.jl: -------------------------------------------------------------------------------- 1 | module ErrorfreeArithmetic 2 | 3 | export add_inorder_errorfree, subtract_inorder_errorfree, 4 | add_errorfree, subtract_errorfree, 5 | square_errorfree, multiply_errorfree, 6 | inv_errorfree, 7 | fma_errorfree, fms_errorfree, 8 | cube_accurately, divide_accurately, sqrt_accurately, invsqrt_accurately 9 | 10 | 11 | const SysFloat = Union{Float64, Float32} # fma must work 12 | 13 | #= single parameter error-free transformations =# 14 | 15 | # 'y' must be negated to get the right result 16 | function inv_errorfree{T<:SysFloat}(a::T) 17 | x = one(T) / a 18 | y = -(fma(x, a, -one(T)) / a) 19 | y = ifelse(y === -zero(T), zero(T), y) 20 | return x, y 21 | end 22 | 23 | 24 | function square_errorfree{T<:SysFloat}(a::T) 25 | x = a * a 26 | y = fma(a, a, -x) 27 | return x, y 28 | end 29 | 30 | function cube_errorfree{T<:SysFloat}(a::T) 31 | p = a*a; e = fma(a, a, -p) 32 | x = p*a; p = fma(p, a, -x) 33 | y = e*a 34 | return x, y 35 | end 36 | 37 | #= two parameter error-free transformations =# 38 | 39 | function add_inorder_errorfree{T<:SysFloat}(a::T, b::T) 40 | x = a + b 41 | y = b - (x - a) 42 | return x, y 43 | end 44 | 45 | function add_errorfree{T<:SysFloat}(a::T, b::T) 46 | x = a + b 47 | t = x - a 48 | y = (a - (x - t)) + (b - t) 49 | return x, y 50 | end 51 | 52 | add_errorfree{T<:SysFloat, R<:Real}(a::T, b::R) = add_errorfree(a, convert(T,b)) 53 | add_errorfree{T<:SysFloat, R<:Real}(a::R, b::T) = add_errorfree(convert(T,a), b) 54 | add_errorfree(a::Real, b::Real) = add_errorfree(float(a), float(b)) 55 | 56 | function subtract_inorder_errorfree{T<:SysFloat}(a::T, b::T) 57 | x = a - b 58 | y = (a - x) - b 59 | return x, y 60 | end 61 | 62 | function subtract_errorfree{T<:SysFloat}(a::T, b::T) 63 | x = a - b 64 | t = x - a 65 | y = (a - (x - t)) - (b + t) 66 | return x, y 67 | end 68 | 69 | subtract_errorfree{T<:SysFloat, R<:Real}(a::T, b::R) = subtract_errorfree(a, convert(T,b)) 70 | subtract_errorfree{T<:SysFloat, R<:Real}(a::R, b::T) = subtract_errorfree(convert(T,a), b) 71 | subtract_errorfree(a::Real, b::Real) = subtract_errorfree(float(a), float(b)) 72 | 73 | 74 | function multiply_errorfree{T<:SysFloat}(a::T, b::T) 75 | x = a * b 76 | y = fma(a, b, -x) 77 | 78 | return x, ifelse(isinf(x), zero(T), y) 79 | end 80 | 81 | multiply_errorfree{T<:SysFloat, R<:Real}(a::T, b::R) = multiply_errorfree(a, convert(T, b)) 82 | multiply_errorfree{T<:SysFloat, R<:Real}(a::R, b::T) = multiply_errorfree(convert(T, a), b) 83 | multiply_errorfree(a::Real, b::Real) = multiply_errorfree(float(a), float(b)) 84 | 85 | #= 86 | divide is as good as possible, not quite errorfree 87 | 88 | "Concerning the divideision, the elementary rounding error is 89 | generally not a floating point number, so it cannot be computed 90 | exactly. Hence we cannot expect to obtain an error 91 | free transformation for the divideision. ... 92 | This means that the computed approximation is as good as 93 | we can expect in the working precision." 94 | -- http://perso.ens-lyon.fr/nicolas.louvet/LaLo05.pdf 95 | function divide{T<:SysFloat}(a::T, b::T) 96 | x = a/b 97 | v = x*b 98 | w = fma(x,b,-v) 99 | y = (a - v - w) / b 100 | x,y 101 | end 102 | =# 103 | # 'y' (iff nonzero) must be negated to get the right result 104 | function divide_accurately{T<:SysFloat}(a::T,b::T) 105 | x = a / b 106 | y = -(fma(x, b,-a) / b) 107 | y = ifelse(y === -zero(T), zero(T), y) 108 | return x, y 109 | end 110 | 111 | #= 112 | While not strictly an error-free transformation it is quite reliable and recommended for use. 113 | Augmented precision square roots, 2-D norms and discussion on correctly reounding sqrt(x^2 + y^2) 114 | by Nicolas Brisebarre, Mioara Joldes, Erik Martin-Dorel, Hean-Michel Muller, Peter Kornerup 115 | =# 116 | function sqrt_accurately{T<:SysFloat}(a::T) 117 | x = sqrt(a) 118 | t = fma(x, -x, a) 119 | y = t / (x+x) 120 | return x, y 121 | end 122 | 123 | function invsqrt_accurately{T<:SysFloat}(a::T) 124 | r = inv(a) 125 | x = sqrt(r) 126 | t = fma(x, -x, r) 127 | y = t / (x+x) 128 | return x, y 129 | end 130 | 131 | 132 | #= three parameter error-free transformations =# 133 | 134 | function add_inorder_errorfree{T<:SysFloat}(a::T,b::T,c::T) 135 | s, t = add_inorder_errorfree(b, c) 136 | x, u = add_inorder_errorfree(a, s) 137 | y, z = add_inorder_errorfree(u, t) 138 | x, y = add_inorder_errorfree(x, y) 139 | return x, y, z 140 | end 141 | 142 | function add_errorfree{T<:SysFloat}(a::T,b::T,c::T) 143 | s, t = add_errorfree(b, c) 144 | x, u = add_errorfree(a, s) 145 | y, z = add_errorfree(u, t) 146 | x, y = add_inorder_errorfree(x, y) 147 | return x, y, z 148 | end 149 | 150 | function multiply_errorfree{T<:SysFloat}(a::T, b::T, c::T) 151 | p, e = multiply_errorfree(a, b) 152 | x, p = multiply_errorfree(p, c) 153 | y, z = multiply_errorfree(e, c) 154 | return x, y, z 155 | end 156 | 157 | function fma_errorfree{T<:SysFloat}(a::T, b::T, c::T) 158 | x = fma(a, b, c) 159 | u1, u2 = multiply_errorfree(a, b) 160 | a1, a2 = add_errorfree(u2, c) 161 | b1, b2 = add_errorfree(u1, a1) 162 | g = (b1 - x) + b2 163 | y, z = add_inorder_errorfree(g, a2) 164 | return x, y, z 165 | end 166 | 167 | function fms_errorfree{T<:SysFloat}(a::T, b::T, c::T) 168 | x = fma(a, b, c) 169 | u1, u2 = multiply_errorfree(a, b) 170 | a1, a2 = subtract_errorfree(u2, c) 171 | b1, b2 = add_errorfree(u1, a1) 172 | g = (b1 - x) + b2 173 | y,z = add_inorder_errorfree(g, a2) 174 | return x, y, z 175 | end 176 | 177 | 178 | # Complex Numbers 179 | #= 180 | Accurate addmation, dot multiplyuct and polynomial evaluation 181 | in complex floating point arithmetic 182 | Stef Graillat ∗, Valérie Ménissier-Morain 183 | Information and Computation 216 (2012) 57–71 184 | http://web.stanford.edu/group/SOL/software/qdotdd/IC2012.pdf 185 | =# 186 | 187 | function add_errorfree{T<:SysFloat}(x::Complex{T}, y::Complex{T}) 188 | rhi, ihi = add_errorfree(x.re, y.re) 189 | rlo, ilo = add_errorfree(x.im, y.im) 190 | return Complex(rhi,rlo), Complex(ihi,ilo) 191 | end 192 | 193 | function subtract_errorfree{T<:SysFloat}(x::Complex{T}, y::Complex{T}) 194 | rhi, ihi = add_errorfree(x.re, -y.re) 195 | rlo, ilo = add_errorfree(x.im, -y.im) 196 | return Complex(rhi,rlo), Complex(ihi,ilo) 197 | end 198 | 199 | 200 | function multiply_errorfree{T<:SysFloat}(x::Complex{T}, y::Complex{T}) 201 | z1, h1 = multiply_errorfree(x.re, y.re) 202 | z2, h2 = multiply_errorfree(x.im, y.im) 203 | z3, h3 = multiply_errorfree(x.re, y.im) 204 | z4, h4 = multiply_errorfree(x.im, y.re) 205 | 206 | z5, h5 = add_errorfree(z1, -z2) 207 | z6, h6 = add_errorfree(z3, z4) 208 | 209 | return Complex(z5,z6), Complex(h1,h3), Complex(-h2,h4), Complex(h5,h6) 210 | end 211 | 212 | 213 | end # module 214 | --------------------------------------------------------------------------------