Skip to content

Commit

Permalink
add RoundingMode argument to convert
Browse files Browse the repository at this point in the history
  • Loading branch information
simonbyrne committed Oct 29, 2014
1 parent 899c7c6 commit 86761e6
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 28 deletions.
5 changes: 5 additions & 0 deletions base/constants.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ convert(::Type{Float16}, x::MathConst) = float16(float32(x))
convert{T<:Real}(::Type{Complex{T}}, x::MathConst) = convert(Complex{T}, convert(T,x))
convert{T<:Integer}(::Type{Rational{T}}, x::MathConst) = convert(Rational{T}, float64(x))

stagedfunction convert{T<:Union(Float32,Float64),s}(t::Type{T},c::MathConst{s},r::RoundingMode)
f = convert(T,big(c()),r())
:($f)
end

=={s}(::MathConst{s}, ::MathConst{s}) = true
==(::MathConst, ::MathConst) = false

Expand Down
13 changes: 11 additions & 2 deletions base/mpfr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import
realmin, realmax, get_rounding, set_rounding, maxintfloat, widen,
significand, frexp

import Base.Rounding: get_rounding_raw, set_rounding_raw

import Base.GMP: ClongMax, CulongMax, CdoubleMax

import Base.Math.lgamma_r
Expand Down Expand Up @@ -118,6 +120,11 @@ convert(::Type{Float64}, x::BigFloat) =
convert(::Type{Float32}, x::BigFloat) =
ccall((:mpfr_get_flt,:libmpfr), Float32, (Ptr{BigFloat},Int32), &x, ROUNDING_MODE[end])

convert(::Type{Float64}, x::BigFloat, r::RoundingMode) =
ccall((:mpfr_get_d,:libmpfr), Float64, (Ptr{BigFloat},Int32), &x, to_mpfr(r))
convert(::Type{Float32}, x::BigFloat, r::RoundingMode) =
ccall((:mpfr_get_flt,:libmpfr), Float32, (Ptr{BigFloat},Int32), &x, to_mpfr(r))

convert(::Type{Integer}, x::BigFloat) = convert(BigInt, x)

promote_rule{T<:Real}(::Type{BigFloat}, ::Type{T}) = BigFloat
Expand Down Expand Up @@ -597,8 +604,10 @@ function from_mpfr(c::Integer)
RoundingMode(c)
end

get_rounding(::Type{BigFloat}) = from_mpfr(ROUNDING_MODE[end])
set_rounding(::Type{BigFloat},r::RoundingMode) = ROUNDING_MODE[end] = to_mpfr(r)
get_rounding_raw(::Type{BigFloat}) = ROUNDING_MODE[end]
set_rounding_raw(::Type{BigFloat},i::Integer) = ROUNDING_MODE[end] = i
get_rounding(::Type{BigFloat}) = from_mpfr(get_rounding_raw(BigFloat))
set_rounding(::Type{BigFloat},r::RoundingMode) = set_rounding_raw(BigFloat,to_mpfr(r))

function copysign(x::BigFloat, y::BigFloat)
z = BigFloat()
Expand Down
17 changes: 13 additions & 4 deletions base/rounding.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module Rounding
include("fenv_constants.jl")

import Base: convert

export
RoundingMode, RoundNearest, RoundToZero, RoundUp, RoundDown, RoundFromZero,
get_rounding, set_rounding, with_rounding
Expand Down Expand Up @@ -34,17 +36,24 @@ function from_fenv(r::Integer)
end
end

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, ()))
set_rounding_raw{T<:Union(Float32,Float64)}(::Type{T},i::Integer) = ccall(:fesetround, Cint, (Cint,), i)
get_rounding_raw{T<:Union(Float32,Float64)}(::Type{T}) = ccall(:fegetround, Cint, ())

set_rounding{T<:Union(Float32,Float64)}(::Type{T},r::RoundingMode) = set_rounding_raw(T,to_fenv(r))
get_rounding{T<:Union(Float32,Float64)}(::Type{T}) = from_fenv(get_rounding_raw(T))

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

convert(::Type{Float32},x::Float64,r::RoundingMode) = with_rounding(Float64,r) do
convert(Float32,x)
end

end #module
119 changes: 99 additions & 20 deletions doc/helpdb.jl
Original file line number Diff line number Diff line change
Expand Up @@ -369,11 +369,44 @@ Any[
"),

("Base","convert","convert(type, x)
("Base","convert","convert(T, x[, r::RoundingMode])
Try to convert \"x\" to the given type. Conversion to a different
numeric type will raise an \"InexactError\" if \"x\" cannot be
represented exactly in the new type.
Convert the \"x\" to a value of type \"T\".
If \"T\" is an \"Integer\" type, an \"InexactError\" will be raised
if \"x\" is not representable by \"T\", for example if \"x\" is not
integer-valued, or is outside the range supported by \"T\".
julia> convert(Int, 3.0)
3
julia> convert(Int, 3.5)
ERROR: InexactError()
in convert at int.jl:185
If \"T\" is a \"FloatingPoint\" or \"Rational\" type, then it will
return the closest value to \"x\" representable by \"T\". If \"T\"
is a \"FloatingPoint\" type that is smaller than that of \"x\",
then the method accepts an optional argument \"r\" which determines
the direction of rounding.
julia> x = 1/3
0.3333333333333333
julia> convert(Float32, x)
0.33333334f0
julia> convert(Rational{Int32}, x)
1//3
julia> convert(Rational{Int64}, x)
6004799503160661//18014398509481984
julia> convert(Float32, x, RoundDown)
0.3333333f0
julia> convert(Float32, x, RoundUp)
0.33333334f0
"),

Expand Down Expand Up @@ -564,10 +597,10 @@ Any[
"),

("Base","fieldtype","fieldtype(value, name::Symbol)
("Base","fieldtype","fieldtype(type, name::Symbol | index::Int)
Determine the declared type of a named field in a value of
composite type.
Determine the declared type of a field (specified by name or index)
in a composite type.
"),

Expand Down Expand Up @@ -1801,15 +1834,15 @@ Any[

("Base","is_valid_ascii","is_valid_ascii(s) -> Bool
Returns true if the string or byte vector is valid ASCII, false
otherwise.
Returns true if the argument (\"ASCIIString\", \"UTF8String\", or
byte vector) is valid ASCII, false otherwise.
"),

("Base","is_valid_utf8","is_valid_utf8(s) -> Bool
Returns true if the string or byte vector is valid UTF-8, false
otherwise.
Returns true if the argument (\"ASCIIString\", \"UTF8String\", or
byte vector) is valid UTF-8, false otherwise.
"),

Expand Down Expand Up @@ -2246,7 +2279,8 @@ Any[

("Base","is_valid_utf16","is_valid_utf16(s) -> Bool
Returns true if the string or \"Uint16\" array is valid UTF-16.
Returns true if the argument (\"UTF16String\" or \"Uint16\" array)
is valid UTF-16.
"),

Expand Down Expand Up @@ -3208,11 +3242,11 @@ Any[

("Base","writedlm","writedlm(f, A, delim='t')
Write \"A\" (a vector, matrix or an iterable collection of
iterable rows) as text to \"f\" (either a filename string or an
\"IO\" stream) using the given delimeter \"delim\" (which defaults
to tab, but can be any printable Julia object, typically a \"Char\"
or \"String\").
Write \"A\" (a vector, matrix or an iterable collection of iterable
rows) as text to \"f\" (either a filename string or an \"IO\"
stream) using the given delimeter \"delim\" (which defaults to tab,
but can be any printable Julia object, typically a \"Char\" or
\"String\").
For example, two vectors \"x\" and \"y\" of the same length can be
written as two columns of tab-delimited text to \"f\" by either
Expand Down Expand Up @@ -6070,9 +6104,22 @@ popdisplay(d::Display)
"),

("Base","cat","cat(dim, A...)
("Base","cat","cat(dims, A...)
Concatenate the input arrays along the specified dimension
Concatenate the input arrays along the specified dimensions in the
iterable \"dims\". For dimensions not in \"dims\", all input arrays
should have the same size, which will also be the size of the
output array along that dimension. For dimensions in \"dims\", the
size of the output array is the sum of the sizes of the input
arrays along that dimension. If \"dims\" is a single number, the
different arrays are tightly stacked along that dimension. If
\"dims\" is an iterable containing several dimensions, this allows
to construct block diagonal matrices and their higher-dimensional
analogues by simultaneously increasing several dimensions for every
new input array and putting zero blocks elsewhere. For example,
*cat([1,2], matrices...)* builds a block diagonal matrix, i.e. a
block matrix with *matrices[1]*, *matrices[2]*, ... as diagonal
blocks and matching zero blocks away from the diagonal.
"),

Expand Down Expand Up @@ -11759,7 +11806,39 @@ Millisecond(v)
\"A\". This includes zeros that are explicitly stored in the sparse
matrix. The returned vector points directly to the internal nonzero
storage of \"A\", and any modifications to the returned vector will
mutate \"A\" as well.
mutate \"A\" as well. See \"rowvals(A)\" and \"nzrange(A, col)\".
"),

("Base","rowvals","rowvals(A)
Return a vector of the row indices of \"A\", and any modifications
to the returned vector will mutate \"A\" as well. Given the
internal storage format of sparse matrices, providing access to how
the row indices are stored internally can be useful in conjuction
with iterating over structural nonzero values. See \"nonzeros(A)\"
and \"nzrange(A, col)\".
"),

("Base","nzrange","nzrange(A, col)
Return the range of indices to the structural nonzero values of a
sparse matrix column. In conjunction with \"nonzeros(A)\" and
\"rowvals(A)\", this allows for convenient iterating over a sparse
matrix
A = sparse(I,J,V)
rows = rowvals(A)
vals = nonzeros(A)
m, n = size(A)
for i = 1:n
for j in nzrange(A, i)
row = rows[j]
val = vals[j]
# perform sparse wizardry...
end
end
"),

Expand Down
44 changes: 42 additions & 2 deletions doc/stdlib/base.rst
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,49 @@ All Objects
With a single symbol argument, tests whether a global variable with that
name is defined in ``current_module()``.

.. function:: convert(type, x)
.. function:: convert(T, x [, r::RoundingMode])

Convert the ``x`` to a value of type ``T``.

If ``T`` is an ``Integer`` type, an ``InexactError`` will be raised if
``x`` is not representable by ``T``, for example if ``x`` is not
integer-valued, or is outside the range supported by ``T``.

.. doctest::

julia> convert(Int, 3.0)
3

julia> convert(Int, 3.5)
ERROR: InexactError()
in convert at int.jl:185

If ``T`` is a ``FloatingPoint`` or ``Rational`` type, then it will return
the closest value to ``x`` representable by ``T``. If ``T`` is a
``FloatingPoint`` type that is smaller than that of ``x``, then the method
accepts an optional argument ``r`` which determines the direction of
rounding.

.. doctest::

julia> x = 1/3
0.3333333333333333

julia> convert(Float32, x)
0.33333334f0

julia> convert(Rational{Int32}, x)
1//3

julia> convert(Rational{Int64}, x)
6004799503160661//18014398509481984

julia> convert(Float32, x, RoundDown)
0.3333333f0

julia> convert(Float32, x, RoundUp)
0.33333334f0

Try to convert ``x`` to the given type. Conversion to a different numeric type will raise an ``InexactError`` if ``x`` cannot be represented exactly in the new type.

.. function:: promote(xs...)

Expand Down
26 changes: 26 additions & 0 deletions test/rounding.jl
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,29 @@ with_rounding(Float32,RoundDown) do
@test a32 - b32 === -c32
@test b32 - a32 === c32
end

# convert with rounding
for v = [sqrt(2),-1/3,nextfloat(1.0),prevfloat(1.0),nextfloat(-1.0),prevfloat(-1.0)]
pn = convert(Float32,v)
@test pn == convert(Float32,v,RoundNearest)
pz = convert(Float32,v,RoundToZero)
pd = convert(Float32,v,RoundDown)
pu = convert(Float32,v,RoundUp)
@test pn == pd || pn == pu
@test v > 0 ? pz == pd : pz == pu
@test pu - pd == eps(pz)
end

for T in [Float32,Float64]
for v in [sqrt(big(2.0)),-big(1.0)/big(3.0),nextfloat(big(1.0)),
pi,e,eulergamma,catalan,golden]
pn = convert(T,v)
@test pn == convert(T,v,RoundNearest)
pz = convert(T,v,RoundToZero)
pd = convert(T,v,RoundDown)
pu = convert(T,v,RoundUp)
@test pn == pd || pn == pu
@test v > 0 ? pz == pd : pz == pu
@test pu - pd == eps(pz)
end
end

0 comments on commit 86761e6

Please sign in to comment.