Arblib.jl icon indicating copy to clipboard operation
Arblib.jl copied to clipboard

default show for Arb might be confusing

Open kalmarek opened this issue 4 years ago • 7 comments

This is an interval containing 0:

julia> let x = sin(Arb(π))
           @info "" x contains(x, 0) Arblib.midref(x) Float64(Arblib.radref(x))
       end
┌ Info: 
│   x = [+/- 2.83e-77]
│   contains(x, 0) = true
│   Arblib.midref(x) = 1.0969174409793520767073235159733943661910882381141135060401716459409529154108e-77
└   Float64(Arblib.radref(x)) = 1.730607217577946e-77

This is an interval containing 1:

julia> let x = sin(Arb(π)/2)
           @info "sin(Arb(π))" x contains(x, 1) Arblib.midref(x) Float64(Arblib.radref(x))
       end
┌ Info: sin(Arb(π))
│   x = [1.000000000000000000000000000000000000000000000000000000000000000000000000000 +/- 1.74e-77]
│   contains(x, 1) = true
│   Arblib.midref(x) = 0.99999999999999999999999999999999999999999999999999999999999999999999999999999
└   Float64(Arblib.radref(x)) = 8.737900781332737e-78

This is an interval containing 1.5:

julia> let x = 1.5sin(Arb(π)/2)
           @info "1.5sin(Arb(π)/2)" x contains(2x, 3) Arblib.midref(x) Float64(Arblib.radref(x))
       end
┌ Info: 1.5sin(Arb(π)/2)
│   x = [1.5000000000000000000000000000000000000000000000000000000000000000000000000000 +/- 4.77e-77]
│   contains(2x, 3) = true
│   Arblib.midref(x) = 1.5
└   Float64(Arblib.radref(x)) = 3.0379188354575523e-77

let's try the same with set_interval!:

julia> let x = Arblib.set_interval!(Arb(), Arf(-0.5), Arf(0.5))
           @info "[-0.5, 0.5]" x contains(x, 0) Arblib.midref(x) Float64(Arblib.radref(x))
       end
┌ Info: [-0.5, 0.5]
│   x = [+/- 0.501]
│   contains(x, 0) = true
│   Arblib.midref(x) = 0
└   Float64(Arblib.radref(x)) = 0.5000000009313226

julia> let x = Arblib.set_interval!(Arb(), Arf(0.5), Arf(1.5))
           @info "[0.5, 1.5]" x contains(x, 1) Arblib.midref(x) Float64(Arblib.radref(x))
       end
┌ Info: [0.5, 1.5]
│   x = [1e+0 +/- 0.501]
│   contains(x, 1) = true
│   Arblib.midref(x) = 1
└   Float64(Arblib.radref(x)) = 0.5000000009313226

julia> let x = Arblib.set_interval!(Arb(), Arf(1.0), Arf(2.0))
           @info "[1.0, 2.0]" x contains(2x, 3) Arblib.midref(x) Float64(Arblib.radref(x))
       end
┌ Info: [1.0, 2.0]
│   x = [+/- 2.01]              ← this is unexpected
│   contains(2x, 3) = true
│   Arblib.midref(x) = 1.5
└   Float64(Arblib.radref(x)) = 0.5000000009313226

moreover:

julia> let x = Arblib.set_interval!(Arb(), Arf(1.0), Arf(2.0))
           @info "[1.0, 2.0]" Arblib.string_decimal(x) Arblib.string_nice(x)
       end
┌ Info: [1.0, 2.0]
│   Arblib.string_decimal(x) = "1.5 +/- 0.5"
└   Arblib.string_nice(x) = "[+/- 2.01]"

kalmarek avatar Oct 31 '20 18:10 kalmarek

What is exactly the bug? My hunch is that in the second and third case the midpoint is not exactly what is printed. Printed is the rounded version. The first I guess is a concise printing. In the other cases the midpoint has an exact representation and is therefore abbreviated.

saschatimme avatar Oct 31 '20 19:10 saschatimme

the bug is that

julia> Arblib.set_interval!(Arb(), Arf(-2.0), Arf(2.0))
[+/- 2.01]

julia> Arblib.set_interval!(Arb(), Arf(1.0), Arf(2.0))
[+/- 2.01]

julia> Arblib.string_decimal(Arblib.set_interval!(Arb(), Arf(1.0), Arf(2.0)))
"1.5 +/- 0.5"

julia> Arblib.string_decimal(Arblib.set_interval!(Arb(), Arf(-2.0), Arf(2.0)))
"0 +/- 2"

the interval [-2.0, 2.0] has the same string_nice (which is used for show) as [1.0, 2.0]

kalmarek avatar Oct 31 '20 20:10 kalmarek

oh I see

saschatimme avatar Oct 31 '20 20:10 saschatimme

I don't think this is a bug but a result of the fact that string_nice only shows correct digits. The Interval [1.0, 2.0] contains both 1 and 2 so not a single digit of the number is known, hence it give anything before the +/- and has to resort to using the radius alone for the result. We get the same result with for example

julia> Arblib.set_interval!(Arb(), Arf(80000), Arf(90000))
[+/- 9.01e+4]

Joel-Dahne avatar Oct 31 '20 20:10 Joel-Dahne

thanks for the explanation @Joel-Dahne; I found it confusing, but it makes perfect sense after explanation; however: given that this requires an explanation should we switch to string_decimal for show?

kalmarek avatar Oct 31 '20 20:10 kalmarek

That is a good point actually! I have actually been bitten a couple of times by this behaviour myself. The main risk I see with this is that reading that string back with arb_get_str is not guaranteed to give an enclosure since for string_decimal, as written in the documentation, "The printed value of the radius is not adjusted to compensate for the fact that the binary-to-decimal conversion of both the midpoint and the radius introduces additional error."

Joel-Dahne avatar Nov 01 '20 09:11 Joel-Dahne

I see, but given the problems with string_nice users should be discouraged from parsing the readable string representation of Arbs:

julia> x = Arblib.set_interval!(Arb(), Arf(1.0), Arf(2.0))
[+/- 2.01]

julia> y = Arb(Arblib.string_nice(x))
[+/- 2.02]

julia> Arblib.midref(y)
0

julia> z = Arb(Arblib.string_decimal(x)) # ← but not guaranteed to enclose x...
[+/- 2.01]

julia> Arblib.midref(z)
1.5

the functions for loading and storing are dump_string and load_string!. This requires additional entry in the docs though.

EDIT: is there a function in julia for serialization into strings? I thought about repr, but reading the docs it doesn't seem to be the one...

kalmarek avatar Nov 01 '20 11:11 kalmarek