├── .gitignore ├── Project.toml ├── src ├── ExactReals.jl ├── arithmetic.jl └── exact_reals.jl ├── .travis.yml ├── .appveyor.yml ├── test └── runtests.jl ├── LICENSE ├── examples └── logistic.jl └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.jl.*.cov 2 | *.jl.cov 3 | *.jl.mem 4 | .DS_Store 5 | /Manifest.toml 6 | /dev/ 7 | -------------------------------------------------------------------------------- /Project.toml: -------------------------------------------------------------------------------- 1 | name = "ExactReals" 2 | uuid = "cc8eb9ea-40c6-483d-ae01-5f60885b8dd9" 3 | authors = ["David P. Sanders"] 4 | version = "0.1.0" 5 | 6 | [compat] 7 | julia = "1" 8 | 9 | [extras] 10 | Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" 11 | 12 | [targets] 13 | test = ["Test"] 14 | -------------------------------------------------------------------------------- /src/ExactReals.jl: -------------------------------------------------------------------------------- 1 | """ 2 | Exact real arithmetic using a Boehm scaled-integer representation. 3 | 4 | Reference: 5 | Keith Briggs, Implementing exact real arithmetic in Python, C++ and C. 6 | *Theor. Comp. Sci.* **351** (2006), 74--81 7 | 8 | """ 9 | module ExactReals 10 | 11 | export ExactReal, value_range 12 | 13 | include("exact_reals.jl") 14 | include("arithmetic.jl") 15 | 16 | 17 | 18 | end # module 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Documentation: http://docs.travis-ci.com/user/languages/julia/ 2 | language: julia 3 | os: 4 | - linux 5 | - osx 6 | julia: 7 | - 1.3 8 | - nightly 9 | matrix: 10 | allow_failures: 11 | - julia: nightly 12 | fast_finish: true 13 | notifications: 14 | email: false 15 | after_success: 16 | - julia -e 'using Pkg; Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())' 17 | -------------------------------------------------------------------------------- /.appveyor.yml: -------------------------------------------------------------------------------- 1 | # Documentation: https://github.com/JuliaCI/Appveyor.jl 2 | environment: 3 | matrix: 4 | - julia_version: 1.3 5 | - julia_version: nightly 6 | platform: 7 | - x86 8 | - x64 9 | matrix: 10 | allow_failures: 11 | - julia_version: nightly 12 | branches: 13 | only: 14 | - master 15 | - /release-.*/ 16 | notifications: 17 | - provider: Email 18 | on_build_success: false 19 | on_build_failure: false 20 | on_build_status_changed: false 21 | install: 22 | - ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1")) 23 | build_script: 24 | - echo "%JL_BUILD_SCRIPT%" 25 | - C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%" 26 | test_script: 27 | - echo "%JL_TEST_SCRIPT%" 28 | - C:\julia\bin\julia -e "%JL_TEST_SCRIPT%" 29 | on_success: 30 | - echo "%JL_CODECOV_SCRIPT%" 31 | - C:\julia\bin\julia -e "%JL_CODECOV_SCRIPT%" 32 | -------------------------------------------------------------------------------- /test/runtests.jl: -------------------------------------------------------------------------------- 1 | using ExactReals 2 | using Test 3 | 4 | @testset "ExactReals.jl" begin 5 | 6 | @testset "Constructors" begin 7 | a = ExactReal(3//5) 8 | @test value_range(a) == (0.6, nextfloat(0.6)) 9 | 10 | b = ExactReal(3) 11 | @test value_range(b) == (prevfloat(3.0), nextfloat(3.0)) 12 | end 13 | 14 | @testset "Arithmetic" begin 15 | 16 | a = ExactReal(3//5) 17 | 18 | b = a + a 19 | @test value_range(b) == (1.2, nextfloat(1.2)) 20 | 21 | c = a * a 22 | @test value_range(c) == (0.36, nextfloat(0.36)) 23 | 24 | d = -a 25 | @test value_range(d) == (prevfloat(-0.6), -0.6) 26 | 27 | e = sqrt(a) 28 | s = sqrt(0.6) 29 | @test value_range(e) == (prevfloat(s), s) 30 | end 31 | 32 | @testset "Comparisons" begin 33 | a = ExactReal(3//5) 34 | @test ExactReals.compare(1 - a, a) == -1 35 | 36 | @test a > ExactReal(1//2) 37 | end 38 | 39 | end 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 David P. Sanders 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /examples/logistic.jl: -------------------------------------------------------------------------------- 1 | 2 | # Example of logistic map from Keith Briggs's 2006 paper 3 | 4 | 5 | using ExactReals 6 | 7 | 8 | "Iterate the function `f` for `n` steps, starting at `x0`." 9 | function iterates(f, n, x0) 10 | 11 | x = x0 12 | 13 | for i in 1:n 14 | x = f(x) 15 | end 16 | 17 | return x 18 | end 19 | 20 | "Logistic map" 21 | logistic(a, x) = a * x * (1 - x) 22 | logistic(a) = x -> logistic(a, x) 23 | 24 | a = rationalize(3.999) 25 | x0 = rationalize(0.9) 26 | n = 54 27 | 28 | y = iterates(logistic(ExactReal(a)), n, ExactReal(x0)) 29 | 30 | @show y > ExactReal(1//2) 31 | 32 | # Floating point: 33 | 34 | z = iterates(logistic(float(a)), n, float(x0)) 35 | 36 | @show z > 0.5 37 | 38 | nothing 39 | 40 | # 41 | # using IntervalArithmetic 42 | # 43 | # x = interval(big"0.9") 44 | # 45 | # iterates(logistic(@biginterval(3.999)), 53, @biginterval(0.9)) |> showfull 46 | # 47 | # iterates(logistic(I"3.999"), 53, I"0.9") 48 | # 49 | # showfull(I"3.999") 50 | # 51 | # # example where double precision is on wrong side of 1/2: 52 | # iterates(logistic(3.999), 54, 0.9) 53 | # 54 | # iterates(logistic(@biginterval(3.999)), 54, @biginterval(0.9)) |> showfull 55 | # 56 | # 57 | # @time y = iterates(logistic(ExactReal(3999//1000)), 54, ExactReal(9//10)) 58 | # 59 | # y > ExactReal(1//2) 60 | -------------------------------------------------------------------------------- /src/arithmetic.jl: -------------------------------------------------------------------------------- 1 | 2 | Base.:+(x::ExactReal, y::ExactReal) = ExactReal(n -> (x(n+2) + y(n+2) + 2) >> 2) 3 | 4 | 5 | Base.:-(x::ExactReal) = ExactReal(n->-x(n)) 6 | Base.:-(x::ExactReal, y::ExactReal) = x + (-y) 7 | Base.:-(x::Union{Rational, Integer}, y::ExactReal) = ExactReal(x) - y 8 | 9 | 10 | 11 | "Most significant digit" 12 | function msd(x::ExactReal) 13 | x.msd > -1 && return x.msd 14 | 15 | n = 0 16 | 17 | while x(n) == 0 18 | n += 1 19 | end 20 | 21 | x.msd = n 22 | 23 | return n 24 | end 25 | 26 | 27 | function Base.:*(x::ExactReal, y::ExactReal) 28 | return ExactReal(n -> begin 29 | p = max(n - msd(y) + 4, n + 3 >> 1) 30 | q = max(n - msd(x) + 4, n + 3 >> 1) 31 | 32 | (1 + x(p) * y(q)) >> (p + q - n) 33 | end 34 | ) 35 | end 36 | 37 | Base.:*(x::Rational, y::ExactReal) = ExactReal(x) * y 38 | 39 | Base.sqrt(x::ExactReal) = ExactReal(n -> isqrt(x(2n))) 40 | 41 | 42 | "Returns -1 if x < y and +1 if x > y" 43 | function compare(x::ExactReal, y::ExactReal) 44 | n = 0 45 | 46 | while true 47 | xn, yn = x(n), y(n) 48 | xn < yn - 1 && return -1 # x < y 49 | xn > yn + 1 && return 1 # x > y 50 | 51 | n += 1 52 | end 53 | end 54 | 55 | 56 | 57 | 58 | Base.:<(x::ExactReal, y::ExactReal) = compare(x, y) == -1 59 | -------------------------------------------------------------------------------- /src/exact_reals.jl: -------------------------------------------------------------------------------- 1 | 2 | "Structure representing a real number that is computable to arbitrary precision." 3 | mutable struct ExactReal <: Real 4 | f::Function 5 | largest_n::Int # largest n for which computed 6 | largest_value::BigInt # value at that n 7 | msd::Int # minimum significant digit 8 | end 9 | 10 | ExactReal(f) = ExactReal(f, -1, -1, -1) 11 | 12 | """ 13 | Memoized evaluation of the nth value in the representation 14 | """ 15 | function (x::ExactReal)(n::Integer) 16 | 17 | if n == x.largest_n 18 | return x.largest_value 19 | 20 | elseif n < x.largest_n 21 | return x.largest_value >> (x.largest_n - n) 22 | 23 | end 24 | 25 | value = (x.f)(n) 26 | 27 | x.largest_n = n 28 | x.largest_value = value 29 | 30 | return value 31 | end 32 | 33 | const B = big(2) # base of the expansion 34 | 35 | 36 | ExactReal(q::Rational) = ExactReal(n -> ((B^n) * numerator(q)) ÷ denominator(q)) 37 | ExactReal(q::Integer) = ExactReal(q//1) 38 | 39 | const exact_real_precision = big(100) 40 | 41 | """ 42 | Returns an interval (as a tuple) containing the exact real number; 43 | this interval is of approximate width `2^(-n)`. 44 | """ 45 | function value_range(x::ExactReal, n=exact_real_precision) 46 | numerator = x(n) 47 | denominator = big(2)^n 48 | 49 | return (Float64( (numerator - 1) / denominator, RoundDown), 50 | Float64( (numerator + 1) / denominator, RoundUp)) 51 | end 52 | 53 | function Base.show(io::IO, r::ExactReal) 54 | lo, hi = value_range(r) 55 | 56 | print(io, "ExactReal in ", 57 | lo, "..", hi) 58 | 59 | end 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ExactReals.jl 2 | 3 | [![Build Status](https://travis-ci.com/dpsanders/ExactReals.jl.svg?branch=master)](https://travis-ci.com/dpsanders/ExactReals.jl) 4 | [![Build Status](https://ci.appveyor.com/api/projects/status/github/dpsanders/ExactReals.jl?svg=true)](https://ci.appveyor.com/project/dpsanders/ExactReals-jl) 5 | [![Codecov](https://codecov.io/gh/dpsanders/ExactReals.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/dpsanders/ExactReals.jl) 6 | 7 | 8 | This is a package for performing exact real arithmetic in Julia. 9 | The implementation uses a fast Cauchy sequence to approximate each real number. 10 | 11 | ## Installation 12 | 13 | To install the package, do 14 | 15 | ```jl 16 | julia> using Pkg 17 | 18 | julia> Pkg.add("https://github.com/dpsanders/ExactReals.jl") 19 | ``` 20 | 21 | ## Usage 22 | 23 | Exact real numbers may be created from integers and rationals. 24 | Basic arithmetic (`+`, `-` and `*`) is implemented: 25 | 26 | ```jl 27 | 28 | julia> using ExactReals 29 | 30 | julia> ExactReal(3) 31 | ExactReal in 2.9999999999999996..3.0000000000000004 32 | 33 | julia> x = ExactReal(3) 34 | ExactReal in 2.9999999999999996..3.0000000000000004 35 | 36 | julia> y = ExactReal(3//5) 37 | ExactReal in 0.6..0.6000000000000001 38 | 39 | julia> x + y 40 | ExactReal in 3.5999999999999996..3.6 41 | 42 | julia> x - y 43 | ExactReal in 2.4..2.4000000000000004 44 | 45 | julia> x * y 46 | ExactReal in 1.7999999999999998..1.8 47 | ``` 48 | 49 | See `examples/logistic.jl` for an example comparing exact real arithmetic to floating-point arithmetic. 50 | 51 | ## Reference 52 | Keith Briggs, Implementing exact real arithmetic in Python, C++ and C. 53 | *Theor. Comp. Sci.* **351** (2006), 74--81 54 | 55 | 56 | ## Author 57 | 58 | - [David P. Sanders](http://sistemas.fciencias.unam.mx/~dsanders), Departamento de Física, Facultad de Ciencias, Universidad Nacional Autónoma de México (UNAM) & Visiting researcher, MIT 59 | 60 | 61 | 62 | ## Acknowledgements 63 | 64 | Financial support is acknowledged from DGAPA-UNAM PAPIIT grant IN-117117 and a *Cátedra Marcos Moshinsky* (2018). 65 | --------------------------------------------------------------------------------