Created
June 22, 2016 09:46
-
-
Save ScottPJones/bdc8c6e2366f711d6bbac337ce243987 to your computer and use it in GitHub Desktop.
WIP on BigFloat replacement
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
module BigFloats | |
export | |
FloatRef, | |
setprecision, | |
BigFlt, Flt, Flt128, Flt256, Flt512 | |
export convert!, idiv!, div!, pow!, add!, sub!, mul!, fma!, neg!, sqrt!, ldexp! | |
import | |
Base: (*), +, -, /, <, <=, ==, >, >=, ^, besselj, besselj0, besselj1, bessely, | |
bessely0, bessely1, ceil, cmp, convert, copysign, div, | |
exp, exp2, exponent, factorial, floor, fma, hypot, isinteger, | |
isfinite, isinf, isnan, ldexp, log, log2, log10, max, min, mod, modf, | |
nextfloat, prevfloat, promote_rule, rem, round, show, | |
sum, sqrt, string, print, trunc, precision, exp10, expm1, | |
gamma, lgamma, digamma, erf, erfc, zeta, eta, log1p, airyai, | |
eps, signbit, sin, cos, tan, sec, csc, cot, acos, asin, atan, | |
cosh, sinh, tanh, sech, csch, coth, acosh, asinh, atanh, atan2, | |
cbrt, typemax, typemin, unsafe_trunc, realmin, realmax, rounding, | |
setrounding, maxintfloat, widen, significand, frexp, tryparse | |
import Base.Rounding: rounding_raw, setrounding_raw | |
import Base.GMP: ClongMax, CulongMax, CdoubleMax, Limb, GMP_BITS_PER_LIMB | |
import Base.Math.lgamma_r | |
# Basic type and initialization definitions | |
type FloatRef <: AbstractFloat | |
prec::Clong | |
sign::Cint | |
exp::Clong | |
d::Ptr{Limb} | |
# Not recommended for general use | |
FloatRef(prec::Clong, sign::Cint, exp::Clong, d::Ptr{Void}) = new(prec, sign, exp, d) | |
end | |
const _RM = Vector{Cint}() | |
const _DP = Vector{Int}() | |
function _fill_fr(prec::Clong = Clong(256)) | |
_res = FloatRef(zero(Clong), zero(Cint), zero(Clong), C_NULL) | |
ccall((:mpfr_init2,:libmpfr), Void, (Ref{FloatRef}, Clong), _res, prec) | |
finalizer(_res, cglobal((:mpfr_clear, :libmpfr))) | |
return _res | |
end | |
_blank_fr() = FloatRef(Clong(256), Cint(1), Clong(0), C_NULL) | |
immutable FloatRegs | |
R::FloatRef | |
X::FloatRef | |
Y::FloatRef | |
Z::FloatRef | |
FloatRegs() = new(_fill_fr(),_blank_fr(),_blank_fr(),_blank_fr()) | |
end | |
const _F = Vector{FloatRegs}(0) | |
@inline RM() = (@inbounds x = _RM[Base.Threads.threadid()]; x) | |
@inline DP() = (@inbounds x = _DP[Base.Threads.threadid()]; x) | |
FloatRef() = _fill_fr(DP()) | |
immutable BigFlt <: AbstractFloat | |
prec::Clong # store sign in high bit of prec | |
exp::Clong | |
d::Vector{Limb} | |
end | |
immutable Flt{N} <: AbstractFloat | |
prec::Clong | |
exp::Clong | |
d::NTuple{N,Limb} | |
end | |
for i = (128, 256, 512) | |
len = div(i+GMP_BITS_PER_LIMB-1, GMP_BITS_PER_LIMB) | |
@eval typealias $(Symbol(:Flt,i)) Flt{$len} | |
end | |
typealias FixedFltTypes Union{BigFlt, Flt128, Flt256, Flt512} | |
typealias FltTypes Union{FloatRef, FixedFltTypes} | |
typealias NumTypes Union{CulongMax, ClongMax, CdoubleMax} | |
#widen(::Type{Float64}) = FloatRef | |
widen{T<:FltTypes}(::Type{T}) = T | |
function __init__() | |
try | |
# set exponent to full range by default | |
set_emin!(get_emin_min()) | |
set_emax!(get_emax_max()) | |
N = Base.Threads.nthreads() | |
resize!(_F, N) | |
resize!(_RM, N) | |
resize!(_DP, N) | |
for i = 1:N | |
_F[i] = FloatRegs() | |
_RM[i] = 0 | |
_DP[i] = 256 | |
end | |
catch ex | |
Base.showerror_nostdio(ex, | |
"WARNING: Error during initialization of module BigFloats") | |
end | |
end | |
@inline function _copy!(dst::FloatRef, x::BigFlt) | |
dst.prec = abs(x.prec) | |
dst.sign = sign(x.prec) | |
dst.exp = x.exp | |
dst.d = pointer(x.d) | |
dst | |
end | |
@inline function BigFlt(z::FloatRef) | |
len = div(z.prec+GMP_BITS_PER_LIMB-1, GMP_BITS_PER_LIMB) | |
x = BigFlt(z.prec*z.sign, z.exp, Vector{Limb}(len)) | |
unsafe_copy!(pointer(x.d), z.d, len) | |
x | |
end | |
@inline function _copy!{T<:Flt}(dst::FloatRef, x::T) | |
dst.prec = abs(x.prec) | |
dst.sign = sign(x.prec) | |
dst.exp = x.exp | |
dst.d = pointer_from_objref(x.d) | |
dst | |
end | |
function Flt(z::FloatRef) | |
N = div(z.prec+GMP_BITS_PER_LIMB-1, GMP_BITS_PER_LIMB) | |
Flt{N}(z.prec*z.sign, z.exp, unsafe_load(reinterpret(Ptr{NTuple{N,Limb}},z.d))) | |
end | |
# Wrap functions that take a BigFlt and return a BigFlt | |
@inline function _wrap1{T<:FixedFltTypes}(f::Function, x::T) | |
local old = gc_enable(false)::Bool | |
try | |
@inbounds _f = _F[Base.Threads.threadid()] | |
f(_f.R, _copy!(_f.X, x)) | |
T(_f.R) | |
finally | |
old && gc_enable(true) | |
end | |
end | |
# Wrap functions that take BigFlt, something not BigFlt and return a BigFlt | |
@inline function _wrap2{T<:FixedFltTypes}(f::Function, x::T, y) | |
local old = gc_enable(false)::Bool | |
try | |
@inbounds _f = _F[Base.Threads.threadid()] | |
f(_f.R, _copy!(_f.X, x), y) | |
T(_f.R) | |
finally | |
old && gc_enable(true) | |
end | |
end | |
# Wrap functions that take 1 to 3 BigFlts with a rounding mode, and return a BigFlt | |
@inline function _wrap_rm1{T<:FixedFltTypes}(f::Function, x::T) | |
local old = gc_enable(false)::Bool | |
try | |
id = Base.Threads.threadid() | |
@inbounds _f = _F[id] | |
@inbounds f(_f.R, _copy!(_f.X, x), _RM[id]) | |
T(_f.R) | |
finally | |
old && gc_enable(true) | |
end | |
end | |
@inline function _wrap_rm2{T<:FixedFltTypes}(f::Function, x::T, y::T) | |
local old = gc_enable(false) | |
try | |
id = Base.Threads.threadid() | |
@inbounds _f = _F[id] | |
@inbounds f(_f.R, _copy!(_f.X, x), _copy!(_f.Y, y), _RM[id]) | |
T(_f.R) | |
finally | |
old && gc_enable(true) | |
end | |
end | |
@inline function _wrap_rm3{T<:FixedFltTypes}(f::Function, x::T, y::T) | |
local old = gc_enable(false) | |
try | |
id = Base.Threads.threadid() | |
@inbounds _f = _F[id] | |
@inbounds f(_f.R, _copy!(_f.X, x), _copy!(_f.Y, y), _copy!(_f.Z, z), _RM[id]) | |
T(_f.R) | |
finally | |
old && gc_enable(true) | |
end | |
end | |
# Wrap functions that take 1 or 2 BigFlts, and return something not a BigFlt | |
@inline function _wrap_c1{T<:FixedFltTypes}(f::Function, x::T) | |
local old = gc_enable(false)::Bool | |
try | |
@inbounds _f = _F[Base.Threads.threadid()] | |
f(_copy!(_f.X, x)) | |
finally | |
old && gc_enable(true) | |
end | |
end | |
@inline function _wrap_c2{T<:FixedFltTypes}(f::Function, x::T, y::T) | |
local old = gc_enable(false) | |
try | |
@inbounds _f = _F[Base.Threads.threadid()] | |
f(_copy!(_f.X, x), _copy!(_f.Y, y)) | |
finally | |
old && gc_enable(true) | |
end | |
end | |
@inline _wrap1(f::Function, x::FloatRef) = (res = FloatRef(); f(res, x); res) | |
@inline _wrap2(f::Function, x::FloatRef, y) = (res = FloatRef(); f(res, x, y); res) | |
@inline _wrap_c1(f::Function, x::FloatRef) = f(x) | |
@inline _wrap_c2(f::Function, x::FloatRef, y::FloatRef) = f(x, y) | |
@inline _wrap_rm1(f::Function, x::FloatRef) = | |
(res = FloatRef(); f(res, x); res) | |
@inline _wrap_rm2(f::Function, x::FloatRef, y::FloatRef) = | |
(res = FloatRef(); f(res, x, y); res) | |
@inline _wrap_rm3(f::Function, x::FloatRef, y::FloatRef, z::FloatRef) = | |
(res = FloatRef(); f(res, x, y); res) | |
convert{T<:FltTypes}(::Type{T}, x::T) = x | |
# convert to FloatRef | |
for (fJ, fC) in ((:si,:Clong), (:ui,:Culong), (:d,:Float64)) | |
@eval begin | |
convert!(res::FloatRef, x::($fC), rm::Cint = RM()) = | |
ccall(($(string(:mpfr_set_,fJ)), :libmpfr), Cint, | |
(Ref{FloatRef}, ($fC), Cint), res, x, rm) | |
end | |
end | |
convert!(res::FloatRef, x::BigInt, rm::Cint = RM()) = | |
ccall((:mpfr_set_z, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{BigInt}, Cint), res, x, rm) | |
convert{T<:FltTypes}(::Type{T}, x::Integer) = T(BigInt(x)) | |
convert(::Type{FloatRef}, x::BigInt) = (res = FloatRef(); convert!(res, x); res) | |
function convert{T<:FixedFltTypes}(::Type{T}, x::BigInt) | |
local old = gc_enable(false)::Bool | |
try | |
id = Base.Threads.threadid() | |
@inbounds _f = _F[id] | |
convert!(_f.R, x, _RM[id]) | |
T(_f.R) | |
finally | |
old && gc_enable(true) | |
end | |
end | |
convert{T<:FltTypes}(::Type{T}, x::Union{Bool,Int8,Int16,Int32}) = T(convert(Clong,x)) | |
convert{T<:FltTypes}(::Type{T}, x::Union{UInt8,UInt16,UInt32}) = T(convert(Culong,x)) | |
convert{T<:FltTypes}(::Type{T}, x::Union{Float16,Float32}) = T(Float64(x)) | |
# Note: this could be optimized for BigFlt | |
convert{T<:FltTypes}(::Type{T}, x::Rational) = T(num(x)) / T(den(x)) | |
function tryparse!(res::FloatRef, s::AbstractString, base::Int=0, rm::Cint = RM()) | |
err = ccall((:mpfr_set_str, :libmpfr), Cint, | |
(Ref{FloatRef}, Cstring, Cint, Cint), res, s, base, rm) | |
err == 0 ? Nullable(res) : Nullable{FloatRef}() | |
end | |
tryparse(::Type{FloatRef}, s::AbstractString) = tryparse!(FloatRef(), s) | |
tryparse(::Type{FloatRef}, s::AbstractString, base::Int) = tryparse!(FloatRef(), s, base) | |
# Make this threadsafe and protect from GC | |
function tryparse{T<:FixedFltTypes}(::Type{T}, s::AbstractString, base::Int=0) | |
local old = gc_enable(false)::Bool | |
try | |
id = Base.Threads.threadid() | |
@inbounds _f = _F[id] | |
err = ccall((:mpfr_set_str, :libmpfr), Cint, | |
(Ref{FloatRef}, Cstring, Cint, Cint), _f.R, s, base, _RM[id]) | |
err == 0 ? Nullable(T(_f.R)) : Nullable{T}() | |
finally | |
old && gc_enable(true) | |
end | |
end | |
convert(::Type{Rational}, x::FloatRef) = convert(Rational{BigInt}, x) | |
#convert(::Type{AbstractFloat}, x::BigInt) = FloatRef(x) | |
## FloatRef -> Integer | |
unsafe_cast(::Type{Int64}, x::FloatRef, ri::Cint) = | |
ccall((:__gmpfr_mpfr_get_sj,:libmpfr), Cintmax_t, | |
(Ref{FloatRef}, Cint), x, ri) | |
unsafe_cast(::Type{UInt64}, x::FloatRef, ri::Cint) = | |
ccall((:__gmpfr_mpfr_get_uj,:libmpfr), Cuintmax_t, | |
(Ref{FloatRef}, Cint), x, ri) | |
function unsafe_cast(::Type{BigInt}, x::FloatRef, ri::Cint) | |
# actually safe, just keep naming consistent | |
_res = BigInt() | |
ccall((:mpfr_get_z, :libmpfr), Cint, (Ref{BigInt}, Ref{FloatRef}, Cint), | |
_res, x, ri) | |
_res | |
end | |
## BigFlt -> Integer | |
function unsafe_cast{T<:FixedFltTypes}(::Type{Int64}, x::T, ri::Cint) | |
local old = gc_enable(false)::Bool | |
try | |
@inbounds _f = _F[Base.Threads.threadid()] | |
ccall((:__gmpfr_mpfr_get_sj,:libmpfr), Cintmax_t, | |
(Ref{FloatRef}, Cint), _copy!(_f.X, x), ri) | |
finally | |
old && gc_enable(true) | |
end | |
end | |
function unsafe_cast{T<:FixedFltTypes}(::Type{UInt64}, x::T, ri::Cint) | |
local old = gc_enable(false)::Bool | |
try | |
@inbounds _f = _F[Base.Threads.threadid()] | |
ccall((:__gmpfr_mpfr_get_uj,:libmpfr), Cuintmax_t, | |
(Ref{FloatRef}, Cint), _copy!(_f.X, x), ri) | |
finally | |
old && gc_enable(true) | |
end | |
end | |
function unsafe_cast{T<:FixedFltTypes}(::Type{BigInt}, x::T, ri::Cint) | |
local old = gc_enable(false)::Bool | |
try | |
@inbounds _f = _F[Base.Threads.threadid()] | |
# actually safe, just keep naming consistent | |
_res = BigInt() | |
ccall((:mpfr_get_z, :libmpfr), Cint, (Ref{BigInt}, Ref{FloatRef}, Cint), | |
_res, _copy!(_f.X, x), ri) | |
_res | |
finally | |
old && gc_enable(true) | |
end | |
end | |
unsafe_cast{T<:Signed}(::Type{T}, x::FltTypes, ri::Cint) = unsafe_cast(Int64, x, ri) % T | |
unsafe_cast{T<:Unsigned}(::Type{T}, x::FltTypes, ri::Cint) = unsafe_cast(UInt64, x, ri) % T | |
unsafe_cast(::Type{Int128}, x::FltTypes, ri::Cint) = Int128(unsafe_cast(BigInt,x,ri)) | |
unsafe_cast(::Type{UInt128}, x::FltTypes, ri::Cint) = UInt128(unsafe_cast(BigInt,x,ri)) | |
unsafe_cast{T<:Integer}(::Type{T}, x::FltTypes, r::RoundingMode) = unsafe_cast(T,x,to_mpfr(r)) | |
unsafe_trunc{T<:Integer}(::Type{T}, x::FltTypes) = unsafe_cast(T,x,RoundToZero) | |
function trunc{T<:Union{Signed,Unsigned}}(::Type{T}, x::FltTypes) | |
(typemin(T) <= x <= typemax(T)) || throw(InexactError()) | |
unsafe_cast(T,x,RoundToZero) | |
end | |
function floor{T<:Union{Signed,Unsigned}}(::Type{T}, x::FltTypes) | |
(typemin(T) <= x <= typemax(T)) || throw(InexactError()) | |
unsafe_cast(T,x,RoundDown) | |
end | |
function ceil{T<:Union{Signed,Unsigned}}(::Type{T}, x::FltTypes) | |
(typemin(T) <= x <= typemax(T)) || throw(InexactError()) | |
unsafe_cast(T,x,RoundUp) | |
end | |
function round{T<:Union{Signed,Unsigned}}(::Type{T}, x::FltTypes) | |
(typemin(T) <= x <= typemax(T)) || throw(InexactError()) | |
unsafe_cast(T,x,RM()) | |
end | |
trunc(::Type{BigInt}, x::FltTypes) = unsafe_cast(BigInt, x, RoundToZero) | |
floor(::Type{BigInt}, x::FltTypes) = unsafe_cast(BigInt, x, RoundDown) | |
ceil(::Type{BigInt}, x::FltTypes) = unsafe_cast(BigInt, x, RoundUp) | |
round(::Type{BigInt}, x::FltTypes) = unsafe_cast(BigInt, x, RM()) | |
# convert/round/trunc/floor/ceil(Integer, x) should return a BigInt | |
trunc(::Type{Integer}, x::FltTypes) = trunc(BigInt, x) | |
floor(::Type{Integer}, x::FltTypes) = floor(BigInt, x) | |
ceil(::Type{Integer}, x::FltTypes) = ceil(BigInt, x) | |
round(::Type{Integer}, x::FltTypes) = round(BigInt, x) | |
convert(::Type{Bool}, x::FltTypes) = x==0 ? false : x==1 ? true : throw(InexactError()) | |
function convert(::Type{BigInt},x::FltTypes) | |
isinteger(x) || throw(InexactError()) | |
trunc(BigInt,x) | |
end | |
function convert{T<:Integer}(::Type{T},x::FltTypes) | |
isinteger(x) || throw(InexactError()) | |
trunc(T,x) | |
end | |
## FloatRef -> AbstractFloat | |
convert(::Type{Float64}, x::FloatRef) = | |
ccall((:mpfr_get_d,:libmpfr), Float64, (Ref{FloatRef},Cint), x, RM()) | |
convert(::Type{Float32}, x::FloatRef) = | |
ccall((:mpfr_get_flt,:libmpfr), Float32, (Ref{FloatRef},Cint), x, RM()) | |
(::Type{Float64})(x::FloatRef, r::RoundingMode) = | |
ccall((:mpfr_get_d,:libmpfr), Float64, (Ref{FloatRef},Cint), x, to_mpfr(r)) | |
(::Type{Float32})(x::FloatRef, r::RoundingMode) = | |
ccall((:mpfr_get_flt,:libmpfr), Float32, (Ref{FloatRef},Cint), x, to_mpfr(r)) | |
## BigFlt -> AbstractFloat | |
function convert{T<:FixedFltTypes}(::Type{Float64}, x::T) | |
local old = gc_enable(false)::Bool | |
try | |
id = Base.Threads.threadid() | |
@inbounds _f = _F[id] | |
ccall((:mpfr_get_d,:libmpfr), Float64, (Ref{FloatRef}, Cint), _copy!(_f.X, x), _RM[id]) | |
finally | |
old && gc_enable(true) | |
end | |
end | |
function convert{T<:FixedFltTypes}(::Type{Float32}, x::T) | |
local old = gc_enable(false)::Bool | |
try | |
id = Base.Threads.threadid() | |
@inbounds _f = _F[id] | |
ccall((:mpfr_get_flt,:libmpfr), Float32, (Ref{FloatRef}, Cint), _copy!(_f.X, x), _RM[id]) | |
finally | |
old && gc_enable(true) | |
end | |
end | |
function (::Type{Float64}){T<:FixedFltTypes}(x::T, r::RoundingMode) | |
local old = gc_enable(false)::Bool | |
try | |
ccall((:mpfr_get_d,:libmpfr), Float64, | |
(Ref{FloatRef}, Cint), _copy!(_F[Base.Threads.threadid()].X, x), to_mpfr(r)) | |
finally | |
old && gc_enable(true) | |
end | |
end | |
function (::Type{Float32}){T<:FixedFltTypes}(x::T, r::RoundingMode) | |
local old = gc_enable(false)::Bool | |
try | |
ccall((:mpfr_get_flt,:libmpfr), Float32, | |
(Ref{FloatRef}, Cint), _copy!(_F[Base.Threads.threadid()].X, x), to_mpfr(r)) | |
finally | |
old && gc_enable(true) | |
end | |
end | |
# TODO: avoid double rounding | |
convert(::Type{Float16}, x::FltTypes) = convert(Float16, convert(Float32, x)) | |
# TODO: avoid double rounding | |
(::Type{Float16})(x::FltTypes, r::RoundingMode) = | |
convert(Float16, Float32(x, r)) | |
promote_rule{T<:Real, S<:FltTypes}(::Type{S}, ::Type{T}) = S | |
promote_rule{T<:AbstractFloat, S<:FltTypes}(::Type{S},::Type{T}) = S | |
#This conflicts with BigFloat definition for now | |
#promote_rule{T<:AbstractFloat}(::Type{BigInt},::Type{T}) = FloatRef | |
function convert(::Type{Rational{BigInt}}, x::FltTypes) | |
isnan(x) && return zero(BigInt)//zero(BigInt) | |
isinf(x) && return copysign(one(BigInt),x)//zero(BigInt) | |
x == 0 && return zero(BigInt) // one(BigInt) | |
s = max(precision(x) - exponent(x), 0) | |
BigInt(ldexp(x,s)) // (BigInt(1) << s) | |
end | |
# Basic arithmetic without promotion | |
for (fJ,fC) in ((:add!, :add), (:mul!, :mul)) | |
@eval begin | |
($fJ)(res::FloatRef, x::FloatRef, y::FloatRef, rm::Cint = RM()) = | |
ccall(($(string(:mpfr_,fC)),:libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Ref{FloatRef}, Cint), | |
res, x, y, rm) | |
end | |
for (fS, T, RT) in ((:_ui, CulongMax, Culong), | |
(:_si, ClongMax, Clong), | |
(:_d, CdoubleMax, Cdouble), | |
(:_z, BigInt, Ref{BigInt})) | |
@eval begin | |
($fJ)(res::FloatRef, x::FloatRef, y::$T, rm::Cint = RM()) = | |
ccall(($(string(:mpfr_,fC)),:libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, $RT, Cint), | |
res, x, y, rm) | |
($fJ)(c::$T, x::FloatRef) = ($fJ)(x,c) | |
end | |
end | |
end | |
sub!(res::FloatRef, c::BigInt, x::FloatRef, rm::Cint = RM()) = | |
ccall((:mpfr_z_sub, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{BigInt}, Ref{FloatRef}, Cint), | |
res, c, x, rm) | |
fma!(res::FloatRef, x::FloatRef, y::FloatRef, z::FloatRef, rm::Cint = RM()) = | |
ccall(("mpfr_fma",:libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Ref{FloatRef}, Ref{FloatRef}, Cint), | |
res, x, y, z, rm) | |
# div | |
for (fC, TX, TY, RX, RY) in ((:div, FloatRef, FloatRef, Ref{FloatRef}, Ref{FloatRef}), | |
(:div_ui, FloatRef, CulongMax, Ref{FloatRef}, Culong), | |
(:ui_div, CulongMax, FloatRef, Culong, Ref{FloatRef}), | |
(:div_si, FloatRef, ClongMax, Ref{FloatRef}, Clong), | |
(:si_div, ClongMax, FloatRef, Clong, Ref{FloatRef}), | |
(:div_d, FloatRef, CdoubleMax, Ref{FloatRef}, Cdouble), | |
(:d_div, CdoubleMax, FloatRef, Cdouble, Ref{FloatRef}), | |
(:div_z, FloatRef, BigInt, Ref{FloatRef}, Ref{BigInt})) | |
@eval begin | |
function idiv!(res::FloatRef, x::$TX, y::$TY) | |
ccall(($(string(:mpfr_,fC)),:libmpfr), Cint, | |
(Ref{FloatRef}, $RX, $RY, Cint), | |
res, x, y, to_mpfr(RoundToZero)) | |
ccall((:mpfr_trunc, :libmpfr), Cint, (Ref{FloatRef}, Ref{FloatRef}), res, res) | |
end | |
end | |
end | |
# / | |
for (fC, TX, TY, RX, RY) in ((:div, FloatRef, FloatRef, Ref{FloatRef}, Ref{FloatRef}), | |
(:div_ui, FloatRef, CulongMax, Ref{FloatRef}, Culong), | |
(:ui_div, CulongMax, FloatRef, Culong, Ref{FloatRef}), | |
(:div_si, FloatRef, ClongMax, Ref{FloatRef}, Clong), | |
(:si_div, ClongMax, FloatRef, Clong, Ref{FloatRef}), | |
(:div_d, FloatRef, CdoubleMax, Ref{FloatRef}, Cdouble), | |
(:d_div, CdoubleMax, FloatRef, Cdouble, Ref{FloatRef}), | |
(:div_z, FloatRef, BigInt, Ref{FloatRef}, Ref{BigInt})) | |
@eval begin | |
function div!(res::FloatRef, x::$TX, y::$TY, rm::Cint = RM()) | |
ccall(($(string(:mpfr_,fC)),:libmpfr), Cint, | |
(Ref{FloatRef}, $RX, $RY, Cint), | |
res, x, y, rm) | |
end | |
end | |
end | |
for (fC, TX, TY, RX, RY) in ((:sub, FloatRef, FloatRef, Ref{FloatRef}, Ref{FloatRef}), | |
(:sub_ui, FloatRef, CulongMax, Ref{FloatRef}, Culong), | |
(:ui_sub, CulongMax, FloatRef, Culong, Ref{FloatRef}), | |
(:sub_si, FloatRef, ClongMax, Ref{FloatRef}, Clong), | |
(:si_sub, ClongMax, FloatRef, Clong, Ref{FloatRef}), | |
(:sub_d, FloatRef, CdoubleMax, Ref{FloatRef}, Cdouble), | |
(:d_sub, CdoubleMax, FloatRef, Cdouble, Ref{FloatRef}), | |
(:sub_z, FloatRef, BigInt, Ref{FloatRef}, Ref{BigInt})) | |
@eval begin | |
function sub!(res::FloatRef, x::$TX, y::$TY, rm::Cint = RM()) | |
ccall(($(string(:mpfr_,fC)),:libmpfr), Cint, | |
(Ref{FloatRef}, $RX, $RY, Cint), | |
res, x, y, rm) | |
end | |
end | |
end | |
# More efficient commutative operations | |
for (fJ, fC) in ((:+, :add!), (:*, :mul!)) | |
@eval begin | |
function ($fJ)(a::FloatRef, b::FloatRef, c::FloatRef) | |
rm = RM() | |
res = FloatRef() | |
$fC(res, a, b, rm) | |
$fC(res, res, c, rm) | |
return res | |
end | |
function ($fJ)(a::FloatRef, b::FloatRef, c::FloatRef, d::FloatRef) | |
rm = RM() | |
res = FloatRef() | |
$fC(res, a, b, rm) | |
$fC(res, res, c, rm) | |
$fC(res, res, d, rm) | |
return res | |
end | |
function ($fJ)(a::FloatRef, b::FloatRef, c::FloatRef, d::FloatRef, e::FloatRef) | |
rm = RM() | |
res = FloatRef() | |
$fC(res, a, b, rm) | |
$fC(res, res, c, rm) | |
$fC(res, res, d, rm) | |
$fC(res, res, e, rm) | |
return res | |
end | |
function ($fJ){T<:FixedFltTypes}(a::T, b::T, c::T) | |
local old = gc_enable(false)::Bool | |
try | |
id = Base.Threads.threadid() | |
@inbounds _f = _F[id] | |
@inbounds rm = _RM[id] | |
$fC(_f.R, _copy!(_f.x, a), _copy!(_f.y, b), rm) | |
$fC(_f.R, _f.R, _copy!(_f.x, c), rm) | |
T(_f.R) | |
finally | |
old && gc_enable(true) | |
end | |
end | |
function ($fJ){T<:FixedFltTypes}(a::T, b::T, c::T, d::T) | |
local old = gc_enable(false)::Bool | |
try | |
id = Base.Threads.threadid() | |
@inbounds _f = _F[id] | |
@inbounds rm = _RM[id] | |
$fC(_f.R, _copy!(_f.x, a), _copy!(_f.y, b), rm) | |
$fC(_f.R, _f.R, _copy!(_f.x, c), rm) | |
$fC(_f.R, _f.R, _copy!(_f.x, d), rm) | |
T(_f.R) | |
finally | |
old && gc_enable(true) | |
end | |
end | |
function ($fJ)(a::BigFlt, b::BigFlt, c::BigFlt, d::BigFlt, e::BigFlt) | |
try | |
id = Base.Threads.threadid() | |
@inbounds _f = _F[id] | |
@inbounds rm = _RM[id] | |
$fC(_f.R, _copy!(_f.x, a), _copy!(_f.y, b), rm) | |
$fC(_f.R, _f.R, _copy!(_f.x, c), rm) | |
$fC(_f.R, _f.R, _copy!(_f.x, d), rm) | |
$fC(_f.R, _f.R, _copy!(_f.x, e), rm) | |
T(_f.R) | |
finally | |
old && gc_enable(true) | |
end | |
end | |
end | |
end | |
neg!(res::FloatRef, x::FloatRef, rm::Cint = RM()) = | |
ccall((:mpfr_neg, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Cint), | |
res, x, rm) | |
function sqrt!(res::FloatRef, x::FloatRef, rm::Cint = RM()) | |
isnan(x) && return x | |
ccall((:mpfr_sqrt, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Cint), | |
res, x, rm) | |
isnan(res) && throw(DomainError()) | |
end | |
-(x::FltTypes) = _wrap_rm1(neg!, x) | |
sqrt(x::FltTypes) = isnan(x) ? x : _wrap_rm1(sqrt!, x) | |
#sqrt(x::BigInt) = sqrt(FloatRef(x)) | |
for (fC, T, RT) in ((Symbol(""), FloatRef, Ref{FloatRef}), | |
(:_ui, CulongMax, Culong), | |
(:_si, ClongMax, Clong), | |
(:_d, CdoubleMax, Cdouble), | |
(:_z, BigInt, Ref{BigInt})) | |
@eval begin | |
pow!(res::FloatRef, x::FloatRef, y::$T, rm::Cint = RM()) = | |
ccall(($(string(:mpfr_pow,fC)),:libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, $RT, Cint), | |
res, x, y, rm) | |
^(x::FltTypes, y::$T) = _wrap_rm2(pow!, x, y) | |
end | |
end | |
^(x::FltTypes, y::Integer) = typemin(Clong) <= y <= typemax(Clong) ? x^Clong(y) : x^BigInt(y) | |
^(x::FltTypes, y::Unsigned) = typemin(Culong) <= y <= typemax(Culong) ? x^Culong(y) : x^BigInt(y) | |
for f in (:exp, :exp2, :exp10, :expm1, :digamma, :erf, :erfc, :zeta, | |
:cosh,:sinh,:tanh,:sech,:csch,:coth, :cbrt) | |
fe = Symbol(f,'!') | |
@eval begin | |
export $fe | |
($fe)(res::FloatRef, x::FloatRef, rm::Cint = RM()) = | |
ccall(($(string(:mpfr_,f)), :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Cint), res, x, rm) | |
end | |
end | |
# return log(2) | |
big_ln2!(res::FloatRef, rm::Cint = RM()) = | |
ccall((:mpfr_const_log2, :libmpfr), Cint, (Ref{FloatRef}, Cint), res, rm) | |
big_ln2() = (res = FloatRef(); big_ln2!(res); res) | |
function eta(x::FloatRef) | |
x == 1 ? big_ln2() : -zeta(x) * expm1(big_ln2()*(1-x)) | |
end | |
ldexp!(res::FloatRef, x::FloatRef, n::Clong, rm::Cint = RM()) = | |
ccall((:mpfr_mul_2si, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Clong, Cint), res, x, n, rm) | |
ldexp!(res::FloatRef, x::FloatRef, n::Culong, rm::Cint = RM()) = | |
ccall((:mpfr_mul_2ui, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Culong, Cint), res, x, n, rm) | |
ldexp(x::FloatRef, n::Union{Clong, Culong}) = (res = FloatRef(); ldexp!(res, x, n); res) | |
ldexp(x::FltTypes, n::ClongMax) = ldexp(x, convert(Clong, n)) | |
ldexp(x::FltTypes, n::CulongMax) = ldexp(x, convert(Culong, n)) | |
ldexp(x::FloatRef, n::Integer) = x*exp2(FloatRef(n)) | |
#ldexp(x::BigFlt, n::Integer) = x*exp2(FloatRef(n)) | |
for (fJ,fC) in ((:airyai, :ai), | |
(:besselj0, :j0), | |
(:besselj1, :j1), | |
(:besselj, :jn)) | |
fe = Symbol(fJ,'!') | |
@eval begin | |
export $fe | |
($fe)(res::FloatRef, x::FloatRef, rm::Cint = RM()) = | |
ccall((string(:mpfr_,$fC), :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Cint), res, x, rm) | |
($fJ)(x::FltTypes) = _wrap_rm1($fe, x) | |
end | |
end | |
for (fJ,fC) in ((:bessely0, :y0), | |
(:bessely1, :y1), | |
(:bessely, :yn)) | |
fe = Symbol(fJ,'!') | |
@eval begin | |
export $fe | |
function ($fe)(res::FloatRef, x::FloatRef, rm::Cint = RM()) | |
x < 0 && throw(DomainError()) | |
ccall((string(:mpfr_,$fC), :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Cint), res, x, rm) | |
end | |
($fJ)(x::FltTypes) = _wrap_rm1($fe, x) | |
end | |
end | |
function factorial!(res::FloatRef, x::FloatRef, rm::Cint = RM()) | |
if x < 0 || !isinteger(x) | |
throw(DomainError()) | |
end | |
ui = convert(Culong, x) | |
ccall((:mpfr_fac_ui, :libmpfr), Cint, | |
(Ref{FloatRef}, Culong, Cint), res, ui, rm) | |
end | |
factorial(x::FltTypes) = _wrap_rm1(factorial!, x) | |
hypot!(res::FloatRef, x::FloatRef, y::FloatRef, rm::Cint = RM()) = | |
ccall((:mpfr_hypot, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Ref{FloatRef}, Cint), res, x, y, rm) | |
hypot{T<:FltTypes}(x::T, y::T) = _wrap_rm2(hypot!, x, y) | |
for f in (:log, :log2, :log10) | |
fe = Symbol(f,'!') | |
@eval begin | |
function ($fe)(res::FloatRef, x::FloatRef, rm::Cint = RM()) | |
x < 0 && throw(DomainError()) | |
ccall(($(string(:mpfr_,f)), :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Cint), res, x, rm) | |
end | |
$f(x::FltTypes) = _wrap_rm1($fe, x) | |
end | |
end | |
function log1p!(res::FloatRef, x::FloatRef, rm::Cint = RM()) | |
x < -1 && throw(DomainError()) | |
ccall((:mpfr_log1p, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Cint), res, x, rm) | |
end | |
max!(res::FloatRef, x::FloatRef, y::FloatRef, rm::Cint = RM()) = | |
ccall((:mpfr_max, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Ref{FloatRef}, Cint), res, x, y, rm) | |
min!(res::FloatRef, x::FloatRef, y::FloatRef, rm::Cint = RM()) = | |
ccall((:mpfr_min, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Ref{FloatRef}, Cint), res, x, y, rm) | |
function modf(x::FloatRef) | |
isinf(x) && return (FloatRef(NaN), x) | |
zint = FloatRef() | |
zfloat = FloatRef() | |
ccall((:mpfr_modf, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Ref{FloatRef}, Cint), zint, zfloat, x, RM()) | |
return (zfloat, zint) | |
end | |
function modf(x::BigFlt) | |
isinf(x) && return (BigFlt(NaN), x) | |
zint = FloatRef() | |
zfloat = FloatRef() | |
ccall((:mpfr_modf, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Ref{FloatRef}, Cint), zint, zfloat, x, RM()) | |
return (BigFlt(zfloat), BigFlt(zint)) | |
end | |
rem!(res::FloatRef, x::FloatRef, y::FloatRef, rm::Cint = RM()) = | |
ccall((:mpfr_fmod, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Ref{FloatRef}, Cint), res, x, y, rm) | |
function sum!(res::FloatRef, arr::AbstractArray{FloatRef}) | |
for val in arr | |
ccall((:mpfr_add, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Ref{FloatRef}, Cint), | |
res, res, val, 0) | |
end | |
end | |
# Functions for which NaN results are converted to DomainError, following Base | |
for f in (:sin,:cos,:tan,:sec,:csc,:acos,:asin,:atan,:acosh,:asinh,:atanh,:gamma) | |
fe = Symbol(f,'!') | |
@eval begin | |
($fe)(res::FloatRef, x::FloatRef, rm::Cint = RM()) = | |
ccall(($(string(:mpfr_,f)), :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Cint), res, x, rm) | |
function ($f)(x::FloatRef) | |
isnan(x) && return x | |
res = FloatRef() | |
($fe)(res, x) | |
isnan(res) && throw(DomainError()) | |
res | |
end | |
function ($f)(x::BigFlt) | |
isnan(x) && return x | |
res = _wrap_rm1($fe, x) | |
isnan(res) && throw(DomainError()) | |
res | |
end | |
end | |
end | |
# log of absolute value of gamma function | |
const lgamma_signp = Array{Cint}(1) | |
lgamma!(res::FloatRef, x::FloatRef, rm::Cint = RM()) = | |
ccall((:mpfr_lgamma,:libmpfr), Cint, | |
(Ref{FloatRef}, Ptr{Cint}, Ref{FloatRef}, Cint), | |
res, lgamma_signp, x, rm) | |
lgamma_r(x::FloatRef) = (lgamma(x), lgamma_signp[1]) | |
atan2!(res::FloatRef, y::FloatRef, x::FloatRef, rm::Cint = RM()) = | |
ccall((:mpfr_atan2, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Ref{FloatRef}, Cint), res, y, x, rm) | |
# Utility functions | |
==(x::FloatRef, y::FloatRef) = | |
ccall((:mpfr_equal_p, :libmpfr), Cint, (Ref{FloatRef}, Ref{FloatRef}), x, y) != 0 | |
<=(x::FloatRef, y::FloatRef) = | |
ccall((:mpfr_lessequal_p, :libmpfr), Cint, (Ref{FloatRef}, Ref{FloatRef}), x, y) != 0 | |
>=(x::FloatRef, y::FloatRef) = | |
ccall((:mpfr_greaterequal_p, :libmpfr), Cint, (Ref{FloatRef}, Ref{FloatRef}), x, y) != 0 | |
<(x::FloatRef, y::FloatRef) = | |
ccall((:mpfr_less_p, :libmpfr), Cint, (Ref{FloatRef}, Ref{FloatRef}), x, y) != 0 | |
>(x::FloatRef, y::FloatRef) = | |
ccall((:mpfr_greater_p, :libmpfr), Cint, (Ref{FloatRef}, Ref{FloatRef}), x, y) != 0 | |
function cmp(x::FloatRef, y::BigInt) | |
isnan(x) && throw(DomainError()) | |
ccall((:mpfr_cmp_z, :libmpfr), Cint, (Ref{FloatRef}, Ref{BigInt}), x, y) | |
end | |
function cmp(x::FloatRef, y::ClongMax) | |
isnan(x) && throw(DomainError()) | |
ccall((:mpfr_cmp_si, :libmpfr), Cint, (Ref{FloatRef}, Clong), x, y) | |
end | |
function cmp(x::FloatRef, y::CulongMax) | |
isnan(x) && throw(DomainError()) | |
ccall((:mpfr_cmp_ui, :libmpfr), Cint, (Ref{FloatRef}, Culong), x, y) | |
end | |
cmp(x::FltTypes, y::Integer) = cmp(x,big(y)) | |
cmp(x::Integer, y::FltTypes) = -cmp(y,x) | |
function cmp(x::FloatRef, y::CdoubleMax) | |
(isnan(x) || isnan(y)) && throw(DomainError()) | |
ccall((:mpfr_cmp_d, :libmpfr), Cint, (Ref{FloatRef}, Cdouble), x, y) | |
end | |
cmp(x::CdoubleMax, y::FltTypes) = -cmp(y,x) | |
==(x::FltTypes, y::Integer) = !isnan(x) && cmp(x,y) == 0 | |
==(x::Integer, y::FltTypes) = y == x | |
==(x::FltTypes, y::CdoubleMax) = !isnan(x) && !isnan(y) && cmp(x,y) == 0 | |
==(x::CdoubleMax, y::FltTypes) = y == x | |
<(x::FltTypes, y::Integer) = !isnan(x) && cmp(x,y) < 0 | |
<(x::Integer, y::FltTypes) = !isnan(y) && cmp(y,x) > 0 | |
<(x::FltTypes, y::CdoubleMax) = !isnan(x) && !isnan(y) && cmp(x,y) < 0 | |
<(x::CdoubleMax, y::FltTypes) = !isnan(x) && !isnan(y) && cmp(y,x) > 0 | |
<=(x::FltTypes, y::Integer) = !isnan(x) && cmp(x,y) <= 0 | |
<=(x::Integer, y::FltTypes) = !isnan(y) && cmp(y,x) >= 0 | |
<=(x::FltTypes, y::CdoubleMax) = !isnan(x) && !isnan(y) && cmp(x,y) <= 0 | |
<=(x::CdoubleMax, y::FltTypes) = !isnan(x) && !isnan(y) && cmp(y,x) >= 0 | |
signbit(x::FloatRef) = ccall((:mpfr_signbit, :libmpfr), Cint, (Ref{FloatRef},), x) != 0 | |
signbit(x::FltTypes) = _wrap1(signbit, x) | |
# precision of an object of type FloatRef | |
precision(x::FloatRef) = ccall((:mpfr_get_prec, :libmpfr), Clong, (Ref{FloatRef},), x) | |
precision(x::BigFlt) = abs(x.prec) | |
precision{T<:Flt}(x::T) = abs(x.prec) | |
precision(::Type{FloatRef}) = DP() # precision of the type FloatRef itself | |
""" | |
setprecision([T=FloatRef,] precision::Int) | |
Set the precision (in bits) to be used for `T` arithmetic. | |
""" | |
function setprecision{T<:FltTypes}(::Type{T}, precision::Int) | |
precision < 2 && throw(DomainError()) | |
@inbounds _DP[Base.Threads.threadid()] = precision | |
precision | |
end | |
setprecision(precision::Int) = setprecision(FloatRef, precision) | |
maxintfloat{T<:FltTypes}(x::T) = T(2)^precision(x) | |
maxintfloat{T<:FltTypes}(::Type{T}) = T(2)^precision(T) | |
to_mpfr(::RoundingMode{:Nearest}) = Cint(0) | |
to_mpfr(::RoundingMode{:ToZero}) = Cint(1) | |
to_mpfr(::RoundingMode{:Up}) = Cint(2) | |
to_mpfr(::RoundingMode{:Down}) = Cint(3) | |
to_mpfr(::RoundingMode{:FromZero}) = Cint(4) | |
function from_mpfr(c::Integer) | |
if c == 0 | |
return RoundNearest | |
elseif c == 1 | |
return RoundToZero | |
elseif c == 2 | |
return RoundUp | |
elseif c == 3 | |
return RoundDown | |
elseif c == 4 | |
return RoundFromZero | |
else | |
throw(ArgumentError("invalid MPFR rounding mode code: $c")) | |
end | |
RoundingMode(c) | |
end | |
rounding_raw{T<:FltTypes}(::Type{T}) = RM() | |
setrounding_raw{T<:FltTypes}(::Type{T},i::Integer) = (_RM[Base.Threads.threadid()] = i) | |
rounding{T<:FltTypes}(::Type{T}) = from_mpfr(rounding_raw(FloatRef)) | |
setrounding{T<:FltTypes}(::Type{T},r::RoundingMode) = setrounding_raw(FloatRef,to_mpfr(r)) | |
copysign!(res::FloatRef, x::FloatRef, y::FloatRef, rm::Cint = RM()) = | |
ccall((:mpfr_copysign, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Ref{FloatRef}, Cint), res, x, y, rm) | |
function exponent(x::FloatRef) | |
if x == 0 || !isfinite(x) | |
throw(DomainError()) | |
end | |
# The '- 1' is to make it work as Base.exponent | |
return ccall((:mpfr_get_exp, :libmpfr), Clong, (Ref{FloatRef},), x) - 1 | |
end | |
function frexp(x::FloatRef) | |
z = FloatRef() | |
c = Clong[0] | |
ccall((:mpfr_frexp, :libmpfr), Cint, | |
(Ptr{Clong}, Ref{FloatRef}, Ref{FloatRef}, Cint), c, z, x, RM()) | |
return (z, c[1]) | |
end | |
function significand!(res::FloatRef, x::FloatRef, rm::Cint = RM()) | |
c = Clong[0] | |
ccall((:mpfr_frexp, :libmpfr), Cint, | |
(Ptr{Clong}, Ref{FloatRef}, Ref{FloatRef}, Cint), c, res, x, rm) | |
# Double the significand to make it work as Base.significand | |
ccall((:mpfr_mul_si, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Clong, Cint), res, res, 2, rm) | |
end | |
isinteger(x::FloatRef) = ccall((:mpfr_integer_p, :libmpfr), Cint, (Ref{FloatRef},), x) != 0 | |
for f in (:ceil, :floor, :trunc) | |
fe = Symbol(f,'!') | |
@eval begin | |
function ($fe)(res::FloatRef, x::FloatRef) | |
ccall(($(string(:mpfr_,f)), :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}), res, x) | |
end | |
end | |
end | |
round!(res::FloatRef, x::FloatRef, rm::Cint = RM()) = | |
ccall((:mpfr_rint, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}, Cint), res, x, rm) | |
round!(res::FloatRef, x::FloatRef, ::RoundingMode{:NearestTiesAway}) = | |
ccall((:mpfr_round, :libmpfr), Cint, | |
(Ref{FloatRef}, Ref{FloatRef}), res, x) | |
isinf(x::FloatRef) = ccall((:mpfr_inf_p, :libmpfr), Cint, (Ref{FloatRef},), x) != 0 | |
isnan(x::FloatRef) = ccall((:mpfr_nan_p, :libmpfr), Cint, (Ref{FloatRef},), x) != 0 | |
isfinite(x::FloatRef) = !isinf(x) && !isnan(x) | |
for fC in (:exponent, :isinteger, :isinf, :isnan, :isfinite) | |
@eval ($fC)(x::BigFlt) = _wrap1($fC, x) | |
end | |
function nextfloat!(res::FloatRef, x::FloatRef, rm::Cint = RM()) | |
ccall((:mpfr_set, :libmpfr), Cint, (Ref{FloatRef}, Ref{FloatRef}, Cint), | |
res, x, rm) | |
ccall((:mpfr_nextabove, :libmpfr), Cint, (Ref{FloatRef},), res) != 0 | |
end | |
function prevfloat!(res::FloatRef, x::FloatRef, rm::Cint = RM()) | |
ccall((:mpfr_set, :libmpfr), Cint, (Ref{FloatRef}, Ref{FloatRef}, Cint), | |
res, x, rm) | |
ccall((:mpfr_nextbelow, :libmpfr), Cint, (Ref{FloatRef},), res) != 0 | |
end | |
# Non-mutating forms that return a new floating value | |
sum(arr::AbstractArray{FloatRef}) = (res = FloatRef(); sum!(res, arr); res) | |
for fT in (:FloatRef, :BigFlt, :Flt) | |
@eval round(x::$fT, ::RoundingMode{:NearestTiesAway}) = | |
_wrap2(round!, x, RoundingMode{:NearestTiesAway}) | |
@eval eps(::Type{$fT}) = nextfloat($fT(1)) - $fT(1) | |
@eval realmin(::Type{$fT}) = nextfloat(zero($fT)) | |
@eval realmax(::Type{$fT}) = prevfloat($fT(Inf)) | |
for (fJ,fC) in ((:+, :add!), (:*, :mul!), (:-, :sub!)) | |
@eval ($fJ){T<:Union{$fT, NumTypes}}(x::$fT, y::T) = _wrap_rm2($fC, x, y) | |
end | |
for (fJ,fC) in ((:+, :add!), (:*, :mul!)) | |
@eval ($fJ)(x::NumTypes, y::$fT) = _wrap_rm2($fC, y, x) | |
end | |
@eval (/){T<:Union{$fT, NumTypes}}(x::$fT, y::T) = _wrap2(div!, x, y) | |
@eval div{T<:Union{$fT, NumTypes}}(x::$fT, y::T) = _wrap2(idiv!, x, y) | |
# Single argument wrapping | |
for f in (:exp, :exp2, :exp10, :expm1, :digamma, :erf, :erfc, :zeta, | |
:cosh,:sinh,:tanh,:sech,:csch,:coth, :cbrt, | |
:log1p, :lgamma, :significand, :round) | |
@eval $f(x::$fT) = _wrap_rm1($(Symbol(f, '!')), x) | |
end | |
# Two argument wrapping | |
for f in (:max, :min, :rem, :atan2, :copysign) | |
@eval $f(x::$fT, y::$fT) = _wrap_rm2($(Symbol(f, '!')), x, y) | |
end | |
# Three argument wrapping | |
@eval fma(x::$fT, y::$fT, z::$fT) = _wrap_rm3(fma!, x, y, z) | |
end | |
""" | |
setprecision(f::Function, [T=FloatRef,] precision::Integer) | |
Change the `T` arithmetic precision (in bits) for the duration of `f`. | |
It is logically equivalent to: | |
old = precision(FloatRef) | |
setprecision(FloatRef, precision) | |
f() | |
setprecision(FloatRef, old) | |
Often used as `setprecision(T, precision) do ... end` | |
""" | |
function setprecision{T}(f::Function, ::Type{T}, prec::Integer) | |
old_prec = precision(T) | |
setprecision(T, prec) | |
try | |
return f() | |
finally | |
setprecision(T, old_prec) | |
end | |
end | |
setprecision(f::Function, precision::Integer) = setprecision(f, FloatRef, precision) | |
for fT in (:FloatRef, :BigFlt, :Flt) | |
@eval typemax(::Type{$fT}) = ($fT)(Inf) | |
@eval typemin(::Type{$fT}) = ($fT)(-Inf) | |
end | |
function string(x::FloatRef) | |
# In general, the number of decimal places needed to read back the number exactly | |
# is, excluding the most significant, ceil(log(10, 2^precision(x))) | |
k = ceil(Cint, precision(x) * 0.3010299956639812) | |
lng = k + Cint(8) # Add space for the sign, the most significand digit, the dot and the exponent | |
buf = Array{UInt8}(lng + 1) | |
# format strings are guaranteed to contain no NUL, so we don't use Cstring | |
lng = ccall((:mpfr_snprintf,:libmpfr), Cint, | |
(Ptr{UInt8}, Culong, Ptr{UInt8}, Ref{FloatRef}...), buf, lng + 1, "%.Re", x) | |
if lng < k + 5 # print at least k decimal places | |
lng = ccall((:mpfr_sprintf,:libmpfr), Cint, | |
(Ptr{UInt8}, Ptr{UInt8}, Ref{FloatRef}...), buf, "%.$(k)Re", x) | |
elseif lng > k + 8 | |
buf = Array{UInt8}(lng + 1) | |
lng = ccall((:mpfr_snprintf,:libmpfr), Cint, | |
(Ptr{UInt8}, Culong, Ptr{UInt8}, Ref{FloatRef}...), buf, lng + 1, "%.Re", x) | |
end | |
n = (1 <= x < 10 || -10 < x <= -1 || x == 0) ? lng - 4 : lng | |
return String(buf[1:n]) | |
end | |
string(x::FixedFltTypes) = _wrap_c1(string, x) | |
print(io::IO, b::FltTypes) = print(io, string(b)) | |
show(io::IO, b::FltTypes) = print(io, string(b)) | |
# get/set exponent min/max | |
get_emax() = ccall((:mpfr_get_emax, :libmpfr), Clong, ()) | |
get_emax_min() = ccall((:mpfr_get_emax_min, :libmpfr), Clong, ()) | |
get_emax_max() = ccall((:mpfr_get_emax_max, :libmpfr), Clong, ()) | |
get_emin() = ccall((:mpfr_get_emin, :libmpfr), Clong, ()) | |
get_emin_min() = ccall((:mpfr_get_emin_min, :libmpfr), Clong, ()) | |
get_emin_max() = ccall((:mpfr_get_emin_max, :libmpfr), Clong, ()) | |
set_emax!(x) = ccall((:mpfr_set_emax, :libmpfr), Void, (Clong,), x) | |
set_emin!(x) = ccall((:mpfr_set_emin, :libmpfr), Void, (Clong,), x) | |
function Base.deepcopy_internal(x::FloatRef, stackdict::ObjectIdDict) | |
haskey(stackdict, x) && return stackdict[x] | |
z = FloatRef(zero(Clong), zero(Cint), zero(Clong), C_NULL) | |
ccall((:mpfr_init2,:libmpfr), Void, (Ref{FloatRef}, Clong), z, x.prec) | |
finalizer(z, cglobal((:mpfr_clear, :libmpfr))) | |
ccall((:mpfr_set, :libmpfr), Cint, (Ref{FloatRef}, Ref{FloatRef}, Cint), | |
z, x, RM()) | |
stackdict[x] = z | |
z | |
end | |
Base.deepcopy_internal(x::Union{Flt,BigFlt}, stackdict::ObjectIdDict) = x | |
end #module |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment