diff --git a/base/mpfr.jl b/base/mpfr.jl index 000d7913112699..272337656ccf7a 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -17,7 +17,8 @@ import cosh, sinh, tanh, sech, csch, coth, acosh, asinh, atanh, cbrt, typemax, typemin, unsafe_trunc, realmin, realmax, rounding, setrounding, maxintfloat, widen, significand, frexp, tryparse, iszero, - isone, big, beta, RefValue + isone, big, beta, RefValue, + _string_n import .Base.Rounding: rounding_raw, setrounding_raw @@ -56,21 +57,44 @@ mutable struct BigFloat <: AbstractFloat sign::Cint exp::Clong d::Ptr{Limb} + # _d::Buffer{Limb} # Julia gc handle for memory @ d + _d::String # Julia gc handle for memory @ d (optimized) + + # Not recommended for general use: + # used internally by, e.g. deepcopy + global function _BigFloat(prec::Clong, sign::Cint, exp::Clong, d::String) + # ccall-based version, inlined below + #z = new(zero(Clong), zero(Cint), zero(Clong), C_NULL, d) + #ccall((:mpfr_custom_init,:libmpfr), Cvoid, (Ptr{Limb}, Clong), d, prec) # currently seems to be a no-op in mpfr + #NAN_KIND = Cint(0) + #ccall((:mpfr_custom_init_set,:libmpfr), Cvoid, (Ref{BigFloat}, Cint, Clong, Ptr{Limb}), z, NAN_KIND, prec, d) + #return z + return new(prec, sign, exp, pointer(d), d) + end function BigFloat() - prec = precision(BigFloat) - z = new(zero(Clong), zero(Cint), zero(Clong), C_NULL) - ccall((:mpfr_init2,:libmpfr), Cvoid, (Ref{BigFloat}, Clong), z, prec) - finalizer(cglobal((:mpfr_clear, :libmpfr)), z) - return z + prec::Clong = precision(BigFloat) + nb = ccall((:mpfr_custom_get_size,:libmpfr), Csize_t, (Clong,), prec) + nb = (nb + Core.sizeof(Limb) - 1) ÷ Core.sizeof(Limb) # align to number of Limb allocations required for this + #d = Vector{Limb}(undef, nb) + d = _string_n(nb * Core.sizeof(Limb)) + EXP_NAN = Clong(1) - Clong(typemax(Culong) >> 1) + return _BigFloat(prec, one(Cint), EXP_NAN, d) # +NAN end +end - # Not recommended for general use: - function BigFloat(prec::Clong, sign::Cint, exp::Clong, d::Ptr{Cvoid}) - new(prec, sign, exp, d) +# overload the definition of unsafe_convert to ensure that `x.d` is assigned +# it may have been dropped in the event that the BigFloat was serialized +Base.unsafe_convert(::Type{Ref{BigFloat}}, x::Ptr{BigFloat}) = x +@inline function Base.unsafe_convert(::Type{Ref{BigFloat}}, x::Ref{BigFloat}) + x = x[] + if x.d == C_NULL + x.d = pointer(x._d) end + return convert(Ptr{BigFloat}, Base.pointer_from_objref(x)) end + """ BigFloat(x) @@ -669,14 +693,14 @@ for f in (:sin, :cos, :tan, :sec, :csc, :acos, :asin, :atan, :acosh, :asinh, :at end # log of absolute value of gamma function -const lgamma_signp = Ref{Cint}() -function lgamma(x::BigFloat) +function lgamma_r(x::BigFloat) z = BigFloat() + lgamma_signp = Ref{Cint}() ccall((:mpfr_lgamma,:libmpfr), Cint, (Ref{BigFloat}, Ref{Cint}, Ref{BigFloat}, Int32), z, lgamma_signp, x, ROUNDING_MODE[]) - return z + return z, lgamma_signp[] end -lgamma_r(x::BigFloat) = (lgamma(x), lgamma_signp[]) +lgamma(x::BigFloat) = lgamma_r(x)[1] function atan(y::BigFloat, x::BigFloat) z = BigFloat() @@ -976,11 +1000,11 @@ set_emin!(x) = ccall((:mpfr_set_emin, :libmpfr), Cvoid, (Clong,), x) function Base.deepcopy_internal(x::BigFloat, stackdict::IdDict) haskey(stackdict, x) && return stackdict[x] - prec = precision(x) - y = BigFloat(zero(Clong), zero(Cint), zero(Clong), C_NULL) - ccall((:mpfr_init2,:libmpfr), Cvoid, (Ref{BigFloat}, Clong), y, prec) - finalizer(cglobal((:mpfr_clear, :libmpfr)), y) - ccall((:mpfr_set, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), y, x, ROUNDING_MODE[]) + # d = copy(x._d) + d = x._d + d′ = GC.@preserve d unsafe_string(pointer(d), sizeof(d)) # creates a definitely-new String + y = _BigFloat(x.prec, x.sign, x.exp, d′) + #ccall((:mpfr_custom_move,:libmpfr), Cvoid, (Ref{BigFloat}, Ptr{Limb}), y, d) # unnecessary stackdict[x] = y return y end diff --git a/stdlib/Serialization/src/Serialization.jl b/stdlib/Serialization/src/Serialization.jl index cf9029a996f47f..a175a2449e4fcd 100644 --- a/stdlib/Serialization/src/Serialization.jl +++ b/stdlib/Serialization/src/Serialization.jl @@ -313,11 +313,6 @@ function serialize(s::AbstractSerializer, n::BigInt) serialize(s, string(n, base = 62)) end -function serialize(s::AbstractSerializer, n::BigFloat) - serialize_type(s, BigFloat) - serialize(s, string(n)) -end - function serialize(s::AbstractSerializer, ex::Expr) serialize_cycle(s, ex) && return l = length(ex.args) @@ -1184,8 +1179,6 @@ function deserialize(s::AbstractSerializer, T::Type{Dict{K,V}}) where {K,V} return t end -deserialize(s::AbstractSerializer, ::Type{BigFloat}) = parse(BigFloat, deserialize(s)) - deserialize(s::AbstractSerializer, ::Type{BigInt}) = parse(BigInt, deserialize(s), base = 62) function deserialize(s::AbstractSerializer, t::Type{Regex}) diff --git a/test/mpfr.jl b/test/mpfr.jl index fab600887ddaca..fbfe83db40ee64 100644 --- a/test/mpfr.jl +++ b/test/mpfr.jl @@ -853,11 +853,17 @@ end @test_throws ArgumentError parse(BigFloat, "1\0") @testset "serialization (issue #12386)" begin - b = IOBuffer() - x = 2.1 * big(pi) - serialize(b, x) - seekstart(b) - @test deserialize(b) == x + b = PipeBuffer() + let x = setprecision(53) do + return 2.1 * big(pi) + end + serialize(b, x) + @test deserialize(b) == x + end + let x = BigFloat(Inf, 46) + serialize(b, x) + @test deserialize(b) == x == BigFloat(Inf, 2) + end end @test isnan(sqrt(BigFloat(NaN))) diff --git a/test/precompile.jl b/test/precompile.jl index 87fff65c96a991..f11ecfafd4aca8 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -142,6 +142,9 @@ try g() = override(1.0) Test.@test g() === 2.0 # compile this + + const abigfloat_f() = big"12.34" + const abigfloat_x = big"43.21" end """) @test_throws ErrorException Core.kwfunc(Base.nothing) # make sure `nothing` didn't have a kwfunc (which would invalidate the attempted test) @@ -164,6 +167,10 @@ try @test Foo.override(1.0e0) == Float64('a') @test Foo.override(1.0f0) == 'b' @test Foo.override(UInt(1)) == 2 + + # Issue #15722 + @test Foo.abigfloat_f()::BigFloat == big"12.34" + @test (Foo.abigfloat_x::BigFloat + 21) == big"64.21" end cachedir = joinpath(dir, "compiled", "v$(VERSION.major).$(VERSION.minor)")