Strange bug when truncating BigFloat to UInt8

I am playing a bit with ridiculously big floating point numbers. The following:

setprecision(8640)

tf = BigFloat(255.11254081778609618826079023493869737962406296545795414517402744704859236140966993143901247046399018605810385229965382224661237237575734540437335370703455101999206434545185652629184342277273809652057173822177535225305169126047961225905508911234185717183743412115200362744894452638970477737566761804028973656927947961862252399541005064976989957545097283625458545512979419419583797514355530824965946083791230028370659489543325462848471362878871531484111771705355370421927291420333769038750965298417800232034805529408061970430135945167766487117170521878186919447606566027745194654377620946145571463604191062057351380190187814870206558909339930024362284560068075769140591856545226007081662383644602389241138632681859686333475654445711994432287161483584642369129765968240018213994716082457344853966797231896348610241546631043535859686756321457284751256086493098862641443858657732581904367195064136501248686643155681279523637299264337783176675396282349504057481698365127663691328899843083225168295969140945633899140964447581239378570797779065826452960758865545939538343404231764422728125818189151076049211759296760897091244735300051423016481098776865512795849977793724965232640460837614245302532492181948719466289970850591632566536134417436318112170859681583333347677160716550527688519599117467062956658812723853289220649136533890537037382896935150138858174206598983271127954064826589338132762912345445855812435520698519879706990589284804017949221992383032754967592504288236010793141827486018147664332825577383438883762724885174207161136882482051998632049044726801257826671978124551852765579167015665773887434392739407031097566831766910765737452829146516243009566328872494671237672962683589385643979094081890464618960154722506169086232468547493552740608612054650768481200391871274313900835204067203524998278788704495062180165125977886128659711956728963862285943077913267931066408662407024257325491518590542379611319583166005779326374299358877528996142167877032390325791826547992399385695290780413507436459751195530697434346107776989785267608149700794090523161417993259100848857216424097582530060801583603897876587777471027634127970796763578516877161490318688249623106665859063291828603118117172361254632116055644541517117743255060400298516722941278131561059281745861575876971661968930101853829232150678662073133619513917280841190069934015199616973203703361765264061308960725804943132237004823409760989946480764708631512464)

trunc(UInt8,tf)

Throws an inexact error! If I truncated first to Int64 and then convert it to UInt8 it works fine. I have no clue what is happening here.

I am using widows (Julia 1.2.0) so it may be a bug of the MPFR library.

Looks buggy to me (also Windows, Julia 1.2.0:

julia> trunc(tf)
255.0

julia> trunc(UInt8, tf)
ERROR: InexactError: trunc(UInt8, 255.112540817786083380269701592624187469482421875)
Stacktrace:
 [1] trunc(::Type{UInt8}, ::BigFloat) at .\mpfr.jl:300
 [2] top-level scope at none:0

julia> trunc(UInt8, 255.113)
0xff

julia>

There seems to be something wrong:

julia> trunc(Int8,255.0)
ERROR: InexactError: trunc(Int8, 255.0)
Stacktrace:
 [1] trunc(::Type{Int8}, ::Float64) at .\float.jl:675
 [2] top-level scope at REPL[18]:1

as expected.
And

julia> trunc(UInt8,255.0)
0xff

also expected.
(ninjad by klaff)

And

julia> trunc(UInt8,BigFloat(255.0))
0xff

but

julia> trunc(UInt8,BigFloat(255.1))
ERROR: InexactError: trunc(UInt8, 255.099999999999994315658113919198513031005859375)
Stacktrace:
 [1] trunc(::Type{UInt8}, ::BigFloat) at .\mpfr.jl:300
 [2] top-level scope at REPL[32]:1

The relevant code in mpfr.jl is:

function trunc(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned}
    (typemin(T) <= x <= typemax(T)) || throw(InexactError(:trunc, T, x))
    unsafe_cast(T, x, RoundToZero)
end
julia> x = BigFloat(255.1)
255.099999999999994315658113919198513031005859375

julia> T=UInt8
UInt8

julia> (typemin(T) <= x <= typemax(T))
false

This explains the InexactError.

But how should it be done? Will have a look into implementation of trunc(Int,Float64)…

The corresponding check in float.jl , line 672 (Julia 1.3.dev):

julia> Float64(typemin(UInt8))-one(Float64) < 255.1 < Float64(typemax(UInt8))+one(Float64)
true

And therefore:

julia> trunc(UInt8,255.1)
0xff

As it should be.

I guess the same can be done with BigFloat, right?

So, the check in mpfr.jl, line 300, should be (perhaps):

function trunc(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned}
    ( BigFloat(typemin(T)-one(BigFloat)) < x < BigFloat(typemax(T)+one(BigFloat)) ) || throw(InexactError(:trunc, T, x))
    unsafe_cast(T, x, RoundToZero)
end

OP, want to open an issue?

removed

This above was just wrong.

Sure, I’ll do

1 Like

How can I overwrite function

function trunc(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned}
    (typemin(T) <= x <= typemax(T)) || throw(InexactError(:trunc, T, x))
    unsafe_cast(T, x, RoundToZero)
end

of mpfr.jl

This seems to be wrong:

julia> bf=BigFloat(255.1)
255.099999999999994315658113919198513031005859375

julia> import Base.MPFR.trunc

julia> function trunc(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned}
                  ( BigFloat(typemin(T)-one(BigFloat)) < x < BigFloat(typemax(T)+one(BigFloat)) ) || throw(InexactError(:trunc, T, x))
                  unsafe_cast(T, x, RoundToZero)
                  end
trunc (generic function with 56 methods)

julia> trunc(UInt8,bf)
ERROR: UndefVarError: unsafe_cast not defined
Stacktrace:
 [1] trunc(::Type{UInt8}, ::BigFloat) at .\REPL[6]:3
 [2] top-level scope at REPL[7]:1

unsafe_trunc is defined:

julia> Base.MPFR.unsafe_cast(UInt8,bf,RoundToZero)
0xff

What am I missing?

Found the solution:
Just import unsafe_trunc too:

julia> import Base.MPFR.unsafe_cast

julia> trunc(UInt8,bf)
0xff

Well, it was nice to talk to me :slight_smile:

1 Like

I think it should probably be:

function trunc(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned}
    (typemin(T) < x+1 && x-1 < typemax(T)) || throw(InexactError(:trunc, T, x))
    unsafe_cast(T, x, RoundToZero)
end

I would be an easy first PR for someone to submit this patch and add couple of tests

@test trunc(UInt8, big(-0.3)) === 0x00
@test trunc(UInt8, big(255.1)) === 0xff