diff --git a/stdlib/Printf/src/Printf.jl b/stdlib/Printf/src/Printf.jl index 1dcd4bae0c2fc..0ebd1c6f62116 100644 --- a/stdlib/Printf/src/Printf.jl +++ b/stdlib/Printf/src/Printf.jl @@ -379,16 +379,34 @@ tofloat(x) = Float64(x) tofloat(x::Base.IEEEFloat) = x tofloat(x::BigFloat) = x +_snprintf(ptr, siz, str, arg) = + @ccall "libmpfr".mpfr_snprintf(ptr::Ptr{UInt8}, siz::Csize_t, str::Ptr{UInt8}; + arg::Ref{BigFloat})::Cint + +const __BIG_FLOAT_MAX__ = 8192 + @inline function fmt(buf, pos, arg, spec::Spec{T}) where {T <: Floats} leftalign, plus, space, zero, hash, width, prec = spec.leftalign, spec.plus, spec.space, spec.zero, spec.hash, spec.width, spec.precision x = tofloat(arg) - if x isa BigFloat && isfinite(x) - ptr = pointer(buf, pos) - newpos = @ccall "libmpfr".mpfr_snprintf(ptr::Ptr{UInt8}, (length(buf) - pos + 1)::Csize_t, string(spec; modifier="R")::Ptr{UInt8}; arg::Ref{BigFloat})::Cint - newpos > 0 || error("invalid printf formatting for BigFloat") - return pos + newpos - elseif x isa BigFloat + if x isa BigFloat + if isfinite(x) + GC.@preserve buf begin + siz = length(buf) - pos + 1 + str = string(spec; modifier="R") + len = _snprintf(pointer(buf, pos), siz, str, x) + if len > siz + maxout = max(__BIG_FLOAT_MAX__, + ceil(Int, precision(x) * log(2) / log(10)) + 25) + len > maxout && + error("Over $maxout bytes $len needed to output BigFloat $x") + resize!(buf, len + 1) + len = _snprintf(pointer(buf, pos), len + 1, str, x) + end + len > 0 || throw(ArgumentError("invalid printf formatting $str for BigFloat")) + return pos + len + end + end x = Float64(x) end if T == Val{'e'} || T == Val{'E'} diff --git a/stdlib/Printf/test/runtests.jl b/stdlib/Printf/test/runtests.jl index 9c341579eb5c7..d3b9d47e7fc45 100644 --- a/stdlib/Printf/test/runtests.jl +++ b/stdlib/Printf/test/runtests.jl @@ -411,6 +411,13 @@ end # Check bug with trailing nul printing BigFloat @test (Printf.@sprintf("%.330f", BigFloat(1)))[end] != '\0' + # Check bugs with truncated output printing BigFloat + @test (Printf.@sprintf("%f", parse(BigFloat, "1e400"))) == + "10000000000000000000000000000000000000000000000000000000000000000000000000000025262527574416492004687051900140830217136998040684679611623086405387447100385714565637522507383770691831689647535911648520404034824470543643098638520633064715221151920028135130764414460468236314621044034960475540018328999334468948008954289495190631358190153259681118693204411689043999084305348398480210026863210192871358464.000000" + + # Check that does not attempt to output incredibly large amounts of digits + @test_throws ErrorException Printf.@sprintf("%f", parse(BigFloat, "1e99999")) + # issue #29662 @test (Printf.@sprintf "%12.3e" pi*1e100) == " 3.142e+100"