Skip to content

Commit

Permalink
add BitsFloat abstract type, leverage dispatch for rounding modes
Browse files Browse the repository at this point in the history
  • Loading branch information
simonbyrne committed Dec 11, 2013
1 parent 43d9445 commit e77e7eb
Show file tree
Hide file tree
Showing 9 changed files with 48 additions and 60 deletions.
6 changes: 5 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,18 @@ Deprecated or removed

* The `Stat` type is renamed `StatStruct` ([#4670])

* `set_rounding`, `get_rounding` and `with_rounding` now take an additional
argument specifying the floating point type to which they apply. The old
behaviour and `[get/set/with]_bigfloat_rounding` functions are deprecated ([#5007])

[#4775]: https://github.com/JuliaLang/julia/issues/4775
[#4870]: https://github.com/JuliaLang/julia/issues/4870
[#4766]: https://github.com/JuliaLang/julia/issues/4766
[#4782]: https://github.com/JuliaLang/julia/issues/4782
[#4759]: https://github.com/JuliaLang/julia/issues/4759
[#4819]: https://github.com/JuliaLang/julia/issues/4819
[#4670]: https://github.com/JuliaLang/julia/issues/4670

[#5007]: https://github.com/JuliaLang/julia/issues/5007


Julia v0.2.0 Release Notes
Expand Down
8 changes: 8 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -359,5 +359,13 @@ const Stat = StatStruct
export CharString
const CharString = UTF32String

@deprecate set_rounding(r::RoundingMode) set_rounding(Float64,r)
@deprecate get_rounding() get_rounding(Float64)
@deprecate with_rounding(f::Function, r::RoundingMode) with_rounding(f::Function, Float64, r)

@deprecate set_bigfloat_rounding(r::RoundingMode) set_rounding(BigFloat,r)
@deprecate get_bigfloat_rounding() get_rounding(BigFloat)
@deprecate with_bigfloat_rounding(f::Function, r::RoundingMode) with_rounding(f::Function, BigFloat, r)

# 0.3 discontinued functions

3 changes: 0 additions & 3 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -842,9 +842,6 @@ export
get_bigfloat_precision,
set_bigfloat_precision,
with_bigfloat_precision,
get_bigfloat_rounding,
set_bigfloat_rounding,
with_bigfloat_rounding,
get_rounding,
set_rounding,
with_rounding,
Expand Down
21 changes: 4 additions & 17 deletions base/mpfr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ export
BigFloat,
get_bigfloat_precision,
set_bigfloat_precision,
with_bigfloat_precision,
set_bigfloat_rounding,
get_bigfloat_rounding,
with_bigfloat_rounding
with_bigfloat_precision

import
Base: (*), +, -, /, <, <=, ==, >, >=, ^, besselj, besselj0, besselj1, bessely,
Expand All @@ -20,7 +17,7 @@ import
itrunc, eps, signbit, sin, cos, tan, sec, csc, cot, acos, asin, atan,
cosh, sinh, tanh, sech, csch, coth, acosh, asinh, atanh, atan2,
serialize, deserialize, inf, nan, hash, cbrt, typemax, typemin,
realmin, realmax
realmin, realmax, get_rounding, set_rounding

import Base.Math.lgamma_r

Expand Down Expand Up @@ -615,8 +612,8 @@ function from_mpfr(c::Integer)
RoundingMode(c)
end

get_bigfloat_rounding() = from_mpfr(ROUNDING_MODE[end])
set_bigfloat_rounding(r::RoundingMode) = ROUNDING_MODE[end] = to_mpfr(r)
get_rounding(::Type{BigFloat}) = from_mpfr(ROUNDING_MODE[end])
set_rounding(::Type{BigFloat},r::RoundingMode) = ROUNDING_MODE[end] = to_mpfr(r)

function copysign(x::BigFloat, y::BigFloat)
z = BigFloat()
Expand Down Expand Up @@ -701,16 +698,6 @@ function with_bigfloat_precision(f::Function, precision::Integer)
end
end

function with_bigfloat_rounding(f::Function, rounding::RoundingMode)
old_rounding = get_bigfloat_rounding()
set_bigfloat_rounding(rounding)
try
return f()
finally
set_bigfloat_rounding(old_rounding)
end
end

function string(x::BigFloat)
lng = 128
for i = 1:2
Expand Down
12 changes: 6 additions & 6 deletions base/rounding.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,16 @@ function from_fenv(r::Integer)
end
end

set_rounding(r::RoundingMode) = ccall(:fesetround, Cint, (Cint,), to_fenv(r))
get_rounding() = from_fenv(ccall(:fegetround, Cint, ()))
set_rounding{T<:Union(Float32,Float64)}(::Type{T},r::RoundingMode) = ccall(:fesetround, Cint, (Cint,), to_fenv(r))
get_rounding{T<:Union(Float32,Float64)}(::Type{T}) = from_fenv(ccall(:fegetround, Cint, ()))

function with_rounding(f::Function, rounding::RoundingMode)
old_rounding = get_rounding()
set_rounding(rounding)
function with_rounding{T}(f::Function, ::Type{T}, rounding::RoundingMode)
old_rounding = get_rounding(T)
set_rounding(T,rounding)
try
return f()
finally
set_rounding(old_rounding)
set_rounding(T,old_rounding)
end
end

Expand Down
6 changes: 3 additions & 3 deletions doc/manual/integers-and-floating-point-numbers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ presented in the `IEEE 754 standard <http://en.wikipedia.org/wiki/IEEE_754-2008>
julia> 1.1 + 0.1
1.2000000000000002

julia> with_rounding(RoundDown) do
julia> with_rounding(Float64,RoundDown) do
1.1 + 0.1
end
1.2
Expand Down Expand Up @@ -603,12 +603,12 @@ will take these changes in account:

.. doctest::

julia> with_bigfloat_rounding(RoundUp) do
julia> with_rounding(BigFloat,RoundUp) do
BigFloat(1) + BigFloat("0.1")
end
1.100000000000000000000000000000000000000000000000000000000000000000000000000003e+00 with 256 bits of precision

julia> with_bigfloat_rounding(RoundDown) do
julia> with_rounding(BigFloat,RoundDown) do
BigFloat(1) + BigFloat("0.1")
end
1.099999999999999999999999999999999999999999999999999999999999999999999999999986e+00 with 256 bits of precision
Expand Down
34 changes: 13 additions & 21 deletions doc/stdlib/base.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3135,22 +3135,26 @@ Numbers
``BigFloat(2.1)`` may not yield what you expect. You may prefer to
initialize constants using strings, e.g., ``BigFloat("2.1")``.

.. function:: get_rounding()
.. function:: get_rounding(T)

Get the current floating point rounding mode. Valid modes are ``RoundNearest``, ``RoundToZero``, ``RoundUp`` and ``RoundDown``.
Get the current floating point rounding mode for type ``T``. Valid modes
are ``RoundNearest``, ``RoundToZero``, ``RoundUp``, ``RoundDown``, and
``RoundFromZero`` (``BigFloat`` only).

.. function:: set_rounding(mode)
.. function:: set_rounding(T, mode)

Set the floating point rounding mode. See ``get_rounding`` for available modes
Set the rounding mode of floating point type ``T``. Note that this may
affect other types, for instance changing the rounding mode of ``Float64``
will change the rounding mode of ``Float32``. See ``get_rounding`` for available modes

.. function:: with_rounding(f::Function,mode)
.. function:: with_rounding(f::Function, T, mode)

Change the floating point rounding mode for the duration of ``f``. It is logically equivalent to::
Change the rounding mode of floating point type ``T`` for the duration of ``f``. It is logically equivalent to::

old = get_rounding()
set_rounding(mode)
old = get_rounding(T)
set_rounding(T, mode)
f()
set_rounding(old)
set_rounding(T, old)

See ``get_rounding`` for available rounding modes.

Expand Down Expand Up @@ -3240,18 +3244,6 @@ The `BigFloat` type implements arbitrary-precision floating-point aritmetic usin
f()
set_bigfloat_precision(old)

.. function:: get_bigfloat_rounding()

Get the current BigFloat rounding mode. Valid modes are ``RoundNearest``, ``RoundToZero``, ``RoundUp``, ``RoundDown``, ``RoundFromZero``

.. function:: set_bigfloat_rounding(mode)

Set the BigFloat rounding mode. See get_bigfloat_rounding for available modes

.. function:: with_bigfloat_rounding(f::Function,mode)

Change the BigFloat rounding mode for the duration of ``f``. See ``get_bigfloat_rounding`` for available rounding modes; see also ``with_bigfloat_precision``.

Random Numbers
--------------

Expand Down
6 changes: 3 additions & 3 deletions test/mpfr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,14 @@ z = BigFloat(30)
# rounding modes
with_bigfloat_precision(4) do
# default mode is round to nearest
down, up = with_bigfloat_rounding(RoundNearest) do
down, up = with_rounding(BigFloat,RoundNearest) do
BigFloat("0.0938"), BigFloat("0.102")
end
with_bigfloat_rounding(RoundDown) do
with_rounding(BigFloat,RoundDown) do
@test BigFloat(0.1) == down
@test BigFloat(0.1) != up
end
with_bigfloat_rounding(RoundUp) do
with_rounding(BigFloat,RoundUp) do
@test BigFloat(0.1) != down
@test BigFloat(0.1) == up
end
Expand Down
12 changes: 6 additions & 6 deletions test/rounding.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ d = prevfloat(1.)
@test b - a === c

# RoundToZero
with_rounding(RoundToZero) do
with_rounding(Float64,RoundToZero) do
@test a + b === d
@test - a - b === -d
@test a - b === -c
Expand All @@ -30,15 +30,15 @@ end
@test b - a == c

# RoundUp
with_rounding(RoundUp) do
with_rounding(Float64,RoundUp) do
@test a + b === 1.
@test - a - b === -d
@test a - b === -c
@test b - a === c
end

# RoundDown
with_rounding(RoundDown) do
with_rounding(Float64,RoundDown) do
@test a + b === d
@test - a - b === -1.
@test a - b === -c
Expand All @@ -59,7 +59,7 @@ d32 = prevfloat(1.0f0)
@test b32 - a32 === c32

# RoundToZero
with_rounding(RoundToZero) do
with_rounding(Float32,RoundToZero) do
@test a32 + b32 === d32
@test - a32 - b32 === -d32
@test a32 - b32 === -c32
Expand All @@ -73,15 +73,15 @@ end
@test b32 - a32 == c32

# RoundUp
with_rounding(RoundUp) do
with_rounding(Float32,RoundUp) do
@test a32 + b32 === 1.0f0
@test - a32 - b32 === -d32
@test a32 - b32 === -c32
@test b32 - a32 === c32
end

# RoundDown
with_rounding(RoundDown) do
with_rounding(Float32,RoundDown) do
@test a32 + b32 === d32
@test - a32 - b32 === -1.0f0
@test a32 - b32 === -c32
Expand Down

6 comments on commit e77e7eb

@StefanKarpinski
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very nice API.

@StefanKarpinski
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So why is it deprecated to call with_rounding without a type? That should set the rounding modes for all floating-point types. Of course it should fail when the rounding mode isn't supported by all types – i.e. RoundFromZero – but for rounding modes that are universally supported (the rest of them), it should just work.

@JeffBezanson
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a fair point, but the reason for the deprecation is probably that "all floating-point types" is not well-defined. New types can add methods to with_rounding, but it seems we'd need some other mechanism to extend the behavior of with_rounding without a type.

@StefanKarpinski
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would require floating-point types registering themselves to support rounding mode changes. This is unavoidable with an API like this that involves global state.

@simonbyrne
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that's exactly the reason why I deprecated it, as I couldn't figure out a neat way to do hooks (I haven't come across them anywhere else in Base). In the end, I couldn't really think of a good use case for when you would want to change both modes, since you can always call set_rounding(typeof(x),RoundDown), or exploit parametric types at dispatch. Also it forces people to think about the types they're using, which is probably a good thing if you're mucking about with rounding modes.

@StefanKarpinski
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can think of a situation: when I'm demoing stuff ;-). But seriously, I feel like this really undermines the genericness of this feature. The idea is that I can switch this and know that all the intervening computations have the rounding mode I just set, regardless of type. That is exactly what you need for the best use case for this, which is Kahan's approach to testing numerical stability: run your program in each rounding mode and see if you get similar results. If you have to go around and nitpick with the rounding mode of each type, then this isn't very helpful.

Please sign in to comment.