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"