Skip to content

Commit 7d60a81

Browse files
committed
More compact BigFloat printing (#23382)
1 parent 4d8f616 commit 7d60a81

File tree

2 files changed

+87
-21
lines changed

2 files changed

+87
-21
lines changed

base/mpfr.jl

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -894,28 +894,63 @@ end
894894

895895
setprecision(f::Function, precision::Integer) = setprecision(f, BigFloat, precision)
896896

897-
function string(x::BigFloat)
898-
isfinite(x) || return string(Float64(x))
899-
900-
# In general, the number of decimal places needed to read back the number exactly
901-
# is, excluding the most significant, ceil(log(10, 2^precision(x)))
902-
k = ceil(Int32, precision(x) * 0.3010299956639812)
903-
lng = k + Int32(8) # Add space for the sign, the most significand digit, the dot and the exponent
904-
buf = Base.StringVector(lng + 1)
905-
# format strings are guaranteed to contain no NUL, so we don't use Cstring
906-
lng = ccall((:mpfr_snprintf,:libmpfr), Int32, (Ptr{UInt8}, Culong, Ptr{UInt8}, Ref{BigFloat}...), buf, lng + 1, "%.Re", x)
907-
if lng < k + 5 # print at least k decimal places
908-
lng = ccall((:mpfr_sprintf,:libmpfr), Int32, (Ptr{UInt8}, Ptr{UInt8}, Ref{BigFloat}...), buf, "%.$(k)Re", x)
909-
elseif lng > k + 8
910-
buf = Base.StringVector(lng + 1)
911-
lng = ccall((:mpfr_snprintf,:libmpfr), Int32, (Ptr{UInt8}, Culong, Ptr{UInt8}, Ref{BigFloat}...), buf, lng + 1, "%.Re", x)
897+
function string_mpfr(x::BigFloat, fmt::String)
898+
buf = Base.StringVector(0)
899+
s = _calculate_buffer_size!(buf, fmt, x)
900+
resize!(buf, s)
901+
_fill_buffer!(buf, fmt, x)
902+
String(buf)
903+
end
904+
905+
function _calculate_buffer_size!(buf, fmt, x::BigFloat)
906+
ccall((:mpfr_snprintf,:libmpfr),
907+
Int32, (Ptr{UInt8}, Culong, Ptr{UInt8}, Ptr{BigFloat}...),
908+
buf, 0, fmt, &x)
909+
end
910+
911+
function _fill_buffer!(buf, fmt, x::BigFloat)
912+
s = length(buf)
913+
# we temporarily need one more item in buffer to capture null termination
914+
resize!(buf, s + 1)
915+
n = ccall((:mpfr_sprintf,:libmpfr), Int32, (Ptr{UInt8}, Ptr{UInt8}, Ptr{BigFloat}...), buf, fmt, &x)
916+
@assert n + 1 == length(buf)
917+
@assert last(buf) == 0x00
918+
resize!(buf, s)
919+
end
920+
921+
function _prettify_bigfloat(s::String)::String
922+
mantissa, exponent = split(s, 'e')
923+
if !contains(mantissa, '.')
924+
mantissa = string(mantissa, '.')
925+
end
926+
mantissa = rstrip(mantissa, '0')
927+
if endswith(mantissa, '.')
928+
mantissa = string(mantissa, '0')
912929
end
913-
n = (1 <= x < 10 || -10 < x <= -1 || iszero(x)) ? lng - 4 : lng
914-
return String(resize!(buf,n))
930+
if exponent == "+00"
931+
mantissa
932+
else
933+
string(mantissa, 'e', exponent)
934+
end
935+
end
936+
937+
function _string(x::BigFloat, fmt::String)::String
938+
isfinite(x) || return string(Float64(x))
939+
_prettify_bigfloat(string_mpfr(x, fmt))
915940
end
941+
_string(x::BigFloat) = _string(x, "%.Re")
942+
_string(x::BigFloat, k::Integer) = _string(x, "%.$(k)Re")
943+
944+
string(b::BigFloat) = _string(b)
916945

917946
print(io::IO, b::BigFloat) = print(io, string(b))
918-
show(io::IO, b::BigFloat) = print(io, string(b))
947+
function show(io::IO, b::BigFloat)
948+
if get(io, :compact, false)
949+
print(io, _string(b, 5))
950+
else
951+
print(io, _string(b))
952+
end
953+
end
919954

920955
# get/set exponent min/max
921956
get_emax() = ccall((:mpfr_get_emax, :libmpfr), Clong, ())

test/mpfr.jl

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -382,17 +382,14 @@ setprecision(406) do
382382
@test string(nextfloat(BigFloat(1))) == str
383383
end
384384
setprecision(21) do
385-
@test string(zero(BigFloat)) == "0.0000000"
386385
@test string(parse(BigFloat, "0.1")) == "1.0000002e-01"
387386
@test string(parse(BigFloat, "-9.9")) == "-9.9000015"
388387
end
389388
setprecision(40) do
390-
@test string(zero(BigFloat)) == "0.0000000000000"
391389
@test string(parse(BigFloat, "0.1")) == "1.0000000000002e-01"
392390
@test string(parse(BigFloat, "-9.9")) == "-9.8999999999942"
393391
end
394392
setprecision(123) do
395-
@test string(zero(BigFloat)) == "0.00000000000000000000000000000000000000"
396393
@test string(parse(BigFloat, "0.1")) == "9.99999999999999999999999999999999999953e-02"
397394
@test string(parse(BigFloat, "-9.9")) == "-9.8999999999999999999999999999999999997"
398395
end
@@ -886,3 +883,37 @@ if MPFR.version() > v"3.1.5" || "r11590" in MPFR.version().build
886883
@test abs(sin(big(pi)/6) - 0.5) < ldexp(big(1.0),-1_999_000)
887884
end
888885
end
886+
887+
@testset "show BigFloat" begin
888+
function test_show_bigfloat(x::BigFloat; contains_e::Bool=true,
889+
ends::String="",
890+
starts::String="")
891+
sx = sprint(show, x)
892+
scx = sprint(showcompact, x)
893+
strx = string(x)
894+
@test sx == strx
895+
@test length(scx) < 20
896+
@test length(scx) <= length(sx)
897+
@test x == parse(BigFloat, sx)
898+
@test (x, parse(BigFloat, scx), rtol=1e-4)
899+
for s in (sx, scx)
900+
@test contains(s, 'e') == contains_e
901+
@test startswith(s, starts)
902+
@test endswith(s, ends)
903+
end
904+
end
905+
906+
test_show_bigfloat(big"1.23456789", contains_e=false, starts="1.23")
907+
test_show_bigfloat(big"-1.23456789", contains_e=false, starts="-1.23")
908+
test_show_bigfloat(big"2.3457645687563543266576889678956787e10000", starts="2.345", ends="e+10000")
909+
test_show_bigfloat(big"-2.3457645687563543266576889678956787e-10000", starts="-2.345", ends="e-10000")
910+
911+
for to_string in [string,
912+
x->sprint(show, x),
913+
x->sprint(showcompact,x)]
914+
@test to_string(big"0.0") == "0.0"
915+
@test to_string(big"-0.0") == "-0.0"
916+
@test to_string(big"1.0") == "1.0"
917+
@test to_string(big"-1.0") == "-1.0"
918+
end
919+
end

0 commit comments

Comments
 (0)