From f2e70d980beae0b0718da5a7e7d28b2818596c06 Mon Sep 17 00:00:00 2001 From: Jacob Quinn Date: Sun, 13 Sep 2020 07:59:16 -0600 Subject: [PATCH] More robust non-integer conversions for %d Printf specifier (#37554) * More robust non-integer conversions for %d Printf specifier Fixes #37552. As pointed out by Jameson, the non-integer conversion code for the `%d` specifier handling code wasn't super robust; I wasn't sure how robust it really needed to be, since it seems a bit sketchy to me for users to be relying on Printf conversion behavior of non-integers with `%d` anyway; C even throws a really scary warning if you pass non-integer for `%d`. But anyways, this isn't that much more machinery and puts back support for printf formatting of `Rational` with `%d` anyway. But a note to users, I'd strongly suggest doing your own conversion, `trunc`, `round`, whatever yourself to ensure you fully understand what's going on if you really need to use the `%d` with non-integer arguments. As for the issue reported, it's note quite as severe a use-case since they're just trying to represent a _huge_ integer via `Float64`, which seems decently reasonable. * Add test for Rational --- stdlib/Printf/src/Printf.jl | 10 ++++++++-- stdlib/Printf/test/runtests.jl | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/stdlib/Printf/src/Printf.jl b/stdlib/Printf/src/Printf.jl index cbec26b303854..e9a9dde910d30 100644 --- a/stdlib/Printf/src/Printf.jl +++ b/stdlib/Printf/src/Printf.jl @@ -262,11 +262,17 @@ end end # integers +toint(x) = x +toint(x::Rational) = Integer(x) +toint(x::AbstractFloat) = x > typemax(Int128) ? + BigInt(round(x)) : x > typemax(Int64) ? + Int128(round(x)) : Int64(round(x)) + @inline function fmt(buf, pos, arg, spec::Spec{T}) where {T <: Ints} leftalign, plus, space, zero, hash, width, prec = spec.leftalign, spec.plus, spec.space, spec.zero, spec.hash, spec.width, spec.precision bs = base(T) - arg2 = arg isa AbstractFloat ? Integer(round(arg)) : arg + arg2 = toint(arg) n = i = ndigits(arg2, base=bs, pad=1) x, neg = arg2 < 0 ? (-arg2, true) : (arg2, false) arglen = n + (neg || (plus | space)) + @@ -675,7 +681,7 @@ function plength(f::Spec{T}, x) where {T <: Strings} end function plength(f::Spec{T}, x) where {T <: Ints} - x2 = x isa AbstractFloat ? Integer(round(x)) : x + x2 = toint(x) return max(f.width, f.precision + ndigits(x2, base=base(T), pad=1) + 5) end diff --git a/stdlib/Printf/test/runtests.jl b/stdlib/Printf/test/runtests.jl index c66e666b38cc0..e7bc1e5c124b1 100644 --- a/stdlib/Printf/test/runtests.jl +++ b/stdlib/Printf/test/runtests.jl @@ -429,6 +429,10 @@ end # 37539 @test @sprintf(" %.1e\n", 0.999) == " 1.0e+00\n" @test @sprintf(" %.1f", 9.999) == " 10.0" + + # 37552 + @test @sprintf("%d", 1.0e100) == "10000000000000000159028911097599180468360808563945281389781327557747838772170381060813469985856815104" + @test @sprintf("%d", 3//1) == "3" end @testset "integers" begin