From 6c1d6f97e590e333eb2356cc103ce3b5fd8fd130 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Tue, 23 May 2017 17:35:45 +0200 Subject: [PATCH 1/5] check overflow in hex2num (fix #22031) --- base/docs/helpdb/Base.jl | 49 --------------------------------- base/essentials.jl | 18 +++++++++++++ base/floatfuncs.jl | 19 ++++++------- base/int.jl | 8 ++++++ base/intfuncs.jl | 58 +++++++++++++++++++++++++++++++++++++++- base/multinverses.jl | 5 +++- test/floatfuncs.jl | 2 ++ test/intfuncs.jl | 8 ++++++ 8 files changed, 105 insertions(+), 62 deletions(-) diff --git a/base/docs/helpdb/Base.jl b/base/docs/helpdb/Base.jl index a3f2530e4fef3..97618d987910b 100644 --- a/base/docs/helpdb/Base.jl +++ b/base/docs/helpdb/Base.jl @@ -76,21 +76,6 @@ Subtraction operator. """ -(x, y) -""" - bits(n) - -A string giving the literal bit representation of a number. - -```jldoctest -julia> bits(4) -"0000000000000000000000000000000000000000000000000000000000000100" - -julia> bits(2.2) -"0100000000000001100110011001100110011001100110011001100110011010" -``` -""" -bits - """ getindex(type[, elements...]) @@ -565,18 +550,6 @@ original string, otherwise they must be from distinct character ranges. """ eachmatch -""" - num2hex(f) - -Get a hexadecimal string of the binary representation of a floating point number. - -```jldoctest -julia> num2hex(2.2) -"400199999999999a" -``` -""" -num2hex - """ truncate(file,n) @@ -1121,13 +1094,6 @@ julia> bin(bswap(1)) """ bswap -""" - maxintfloat(T) - -The largest integer losslessly representable by the given floating-point DataType `T`. -""" -maxintfloat - """ delete!(collection, key) @@ -1360,13 +1326,6 @@ false """ isempty -""" - hex2num(str) - -Convert a hexadecimal string to the floating point number it represents. -""" -hex2num - """ InexactError() @@ -2165,14 +2124,6 @@ Values for `String` can be of that type, or `Vector{UInt8}`. """ isvalid(T,value) -""" - unsigned(x) -> Unsigned - -Convert a number to an unsigned integer. If the argument is signed, it is reinterpreted as -unsigned without checking for negative values. -""" -unsigned - """ reverseind(v, i) diff --git a/base/essentials.jl b/base/essentials.jl index 6e06e23302d1d..11619fb49ff1c 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -138,6 +138,24 @@ convert(::Type{T}, x::T) where {T<:Tuple{Any,Vararg{Any}}} = x oftype(x,c) = convert(typeof(x),c) +""" + unsigned(x) -> Unsigned + +Convert a number to an unsigned integer. If the argument is signed, it is reinterpreted as +unsigned without checking for negative values. + + unsigned(T::Type) -> UnsignedType + +Return the return-type of unsigned(x::T). + +```jldoctest +julia> unsigned(12) +0x000000000000000c + +julia> unsigned(Int) +UInt64 +``` +""" unsigned(x::Int) = reinterpret(UInt, x) signed(x::UInt) = reinterpret(Int, x) diff --git a/base/floatfuncs.jl b/base/floatfuncs.jl index a5973c7d99986..9f8384835edcf 100644 --- a/base/floatfuncs.jl +++ b/base/floatfuncs.jl @@ -16,6 +16,11 @@ signbit(x::Float64) = signbit(bitcast(Int64, x)) signbit(x::Float32) = signbit(bitcast(Int32, x)) signbit(x::Float16) = signbit(bitcast(Int16, x)) +""" + maxintfloat(T) + +The largest integer losslessly representable by the given floating-point DataType `T`. +""" maxintfloat(::Type{Float64}) = 9007199254740992. maxintfloat(::Type{Float32}) = Float32(16777216.) maxintfloat(::Type{Float16}) = Float16(2048f0) @@ -24,18 +29,10 @@ maxintfloat() = maxintfloat(Float64) isinteger(x::AbstractFloat) = (x - trunc(x) == 0) -num2hex(x::Float16) = hex(bitcast(UInt16, x), 4) -num2hex(x::Float32) = hex(bitcast(UInt32, x), 8) -num2hex(x::Float64) = hex(bitcast(UInt64, x), 16) - function hex2num(s::AbstractString) - if length(s) <= 4 - return bitcast(Float16, parse(UInt16, s, 16)) - end - if length(s) <= 8 - return bitcast(Float32, parse(UInt32, s, 16)) - end - return bitcast(Float64, parse(UInt64, s, 16)) + l = length(s) + l > 16 && throw(ArgumentError("the passed string must be of length <= 16, got $l")) + hex2num(l <= 4 ? Float16 : l <= 8 ? Float32 : Float64, s) end """ diff --git a/base/int.jl b/base/int.jl index 19b65d5ea8f94..c568335b9f615 100644 --- a/base/int.jl +++ b/base/int.jl @@ -23,6 +23,14 @@ const BitInteger = Union{BitInteger_types...} const BitSigned64T = Union{Type{Int8}, Type{Int16}, Type{Int32}, Type{Int64}} const BitUnsigned64T = Union{Type{UInt8}, Type{UInt16}, Type{UInt32}, Type{UInt64}} +const BitFloat_types = (Float16, Float32, Float64) +const BitFloat = Union{BitFloat_types...} +const BitReal_types = (BitInteger_types..., BitFloat_types...) +const BitReal = Union{BitReal_types...} + +reinterpret(::Type{Unsigned}, x::BitInteger) = unsigned(x) + + ## integer comparisons ## (<)(x::T, y::T) where {T<:BitSigned} = slt_int(x, y) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 50bb11bd47f59..7fd538a9d9986 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -483,7 +483,48 @@ function hex(x::Unsigned, pad::Int, neg::Bool) String(a) end -num2hex(n::Integer) = hex(n, sizeof(n)*2) +""" + num2hex(f) + +An hexadecimal string of the binary representation of a number. +See also the [`bits`](@ref) function, which is similar but gives +a binary string. + +```jldoctest +julia> num2hex(Int64(4)) +"0000000000000004" + +julia> num2hex(2.2) +"400199999999999a" + +``` +""" +num2hex(n::BitReal) = hex(reinterpret(Unsigned, n), sizeof(n)*2) +num2hex(n::Bool) = hex(n) + +""" + hex2num(T::Type, str) + +Interprets a hexadecimal string as the bit representation of a +number of type `T`. See also [`num2hex`](@ref). + +```jldoctest +julia> hex2num(Float64, "400199999999999a") +2.2 + +julia> hex2num(Int32, "fffffffe") +-2 +``` + + hex2num(str) + +Convert a hexadecimal string to the floating point number it represents. +This function, which is inherently type-unstable, returns a `Float16`, +a `Float32`, or a `Float64` depending on the length of the string `str` +(note that this length must hence be no greater than 16). +""" +hex2num(::Type{T}, s::AbstractString) where {T<:BitReal} = + reinterpret(T, parse(unsigned(T), s, 16)) const base36digits = ['0':'9';'a':'z'] const base62digits = ['0':'9';'A':'Z';'a':'z'] @@ -573,6 +614,21 @@ Convert an integer to a decimal string, optionally specifying a number of digits """ dec +""" + bits(n) + +A string giving the literal bit representation of a number. +See also the [`num2hex`](@ref) function, which is similar but +gives an hexadecimal string. + +```jldoctest +julia> bits(4) +"0000000000000000000000000000000000000000000000000000000000000100" + +julia> bits(2.2) +"0100000000000001100110011001100110011001100110011001100110011010" +``` +""" bits(x::Union{Bool,Int8,UInt8}) = bin(reinterpret(UInt8,x),8) bits(x::Union{Int16,UInt16,Float16}) = bin(reinterpret(UInt16,x),16) bits(x::Union{Char,Int32,UInt32,Float32}) = bin(reinterpret(UInt32,x),32) diff --git a/base/multinverses.jl b/base/multinverses.jl index 5b60e76e40a29..be34348a1a17e 100644 --- a/base/multinverses.jl +++ b/base/multinverses.jl @@ -11,7 +11,10 @@ unsigned(::Type{Int16}) = UInt16 unsigned(::Type{Int32}) = UInt32 unsigned(::Type{Int64}) = UInt64 unsigned(::Type{Int128}) = UInt128 -unsigned{T<:Unsigned}(::Type{T}) = T +unsigned(::Type{T}) where {T<:Unsigned} = T +unsigned(::Type{Float16}) = UInt16 +unsigned(::Type{Float32}) = UInt32 +unsigned(::Type{Float64}) = UInt64 abstract type MultiplicativeInverse{T} end diff --git a/test/floatfuncs.jl b/test/floatfuncs.jl index e246e8df5cac6..96ae477f0fdbd 100644 --- a/test/floatfuncs.jl +++ b/test/floatfuncs.jl @@ -39,7 +39,9 @@ end for elty in (Float16,Float32,Float64), _ = 1:10 x = rand(elty) @test hex2num(num2hex(x)) ≈ x + @test hex2num(elty, num2hex(x)) ≈ x end +@test_throws ArgumentError hex2num(String(rand('a':'f', rand(17:100)))) # round for elty in (Float32,Float64) diff --git a/test/intfuncs.jl b/test/intfuncs.jl index 0c6fc028a11c8..175c0fe5dbbf5 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -124,6 +124,14 @@ end @test hex(12) == "c" @test hex(-12, 3) == "-00c" @test num2hex(1243) == (Int == Int32 ? "000004db" : "00000000000004db") +@test num2hex(-1243) == (Int == Int32 ? "fffffb25" : "fffffffffffffb25") +@test num2hex(true) == "1" +@test num2hex(false) == "0" + +for elty in Base.BitInteger_types, _ = 1:10 + x = rand(elty) + @test hex2num(elty, num2hex(x)) == x +end @test base(2, 5, 7) == "0000101" From b38f3e5e464109236afdd7beb9b0128a778f1f40 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Thu, 25 May 2017 16:34:48 +0200 Subject: [PATCH 2/5] use reinterpret(Unsigned,T) instead of unsigned(T), more docs, more tests --- base/docs/helpdb/Base.jl | 8 -------- base/essentials.jl | 38 ++++++++++++++++++++++++++++++++++++-- base/floatfuncs.jl | 10 +++++++++- base/int.jl | 6 ++++++ base/intfuncs.jl | 31 ++++++++++++++++++++++--------- base/multinverses.jl | 12 +----------- test/floatfuncs.jl | 2 +- test/int.jl | 39 +++++++++++++++++++++++++++++++++------ 8 files changed, 108 insertions(+), 38 deletions(-) diff --git a/base/docs/helpdb/Base.jl b/base/docs/helpdb/Base.jl index 97618d987910b..994dc185744c9 100644 --- a/base/docs/helpdb/Base.jl +++ b/base/docs/helpdb/Base.jl @@ -2248,14 +2248,6 @@ for sets of arbitrary objects. """ Set -""" - signed(x) - -Convert a number to a signed integer. If the argument is unsigned, it is reinterpreted as -signed without checking for overflow. -""" -signed - """ Val{c} diff --git a/base/essentials.jl b/base/essentials.jl index 11619fb49ff1c..20694b2027d80 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -143,20 +143,54 @@ oftype(x,c) = convert(typeof(x),c) Convert a number to an unsigned integer. If the argument is signed, it is reinterpreted as unsigned without checking for negative values. +See also [`signed`](@ref). unsigned(T::Type) -> UnsignedType -Return the return-type of unsigned(x::T). +Return the return-type of `unsigned(x::T)`, so that `unsigned(x)::unsigned(typeof(x))`. ```jldoctest julia> unsigned(12) 0x000000000000000c -julia> unsigned(Int) +julia> unsigned(Int64) UInt64 + +julia> unsigned(2.0) +0x0000000000000002 + +julia> unsigned(2.2) +ERROR: InexactError() +[...] ``` """ unsigned(x::Int) = reinterpret(UInt, x) + +""" + signed(x) + +Convert a number to a signed integer. If the argument is unsigned, it is reinterpreted as +signed without checking for overflow. +See also [`unsigned`](@ref). + + signed(T::Type) -> SignedType + +Return the return-type of `signed(x::T)`, so that `signed(x)::signed(typeof(x))`. + +```jldoctest +julia> signed(0xc) +12 + +julia> signed(UInt64) +Int64 + +julia> signed(2.0) +2 + +julia> signed(2.2) +ERROR: InexactError() +[...] +""" signed(x::UInt) = reinterpret(Int, x) # conversions used by ccall diff --git a/base/floatfuncs.jl b/base/floatfuncs.jl index 9f8384835edcf..50dcfb44b32e2 100644 --- a/base/floatfuncs.jl +++ b/base/floatfuncs.jl @@ -29,9 +29,17 @@ maxintfloat() = maxintfloat(Float64) isinteger(x::AbstractFloat) = (x - trunc(x) == 0) +""" + hex2num(str) + +Convert a hexadecimal string to the floating point number it represents. +This function, which is inherently type-unstable, returns a `Float16`, +a `Float32`, or a `Float64` depending on the length of the string `str` +(note that this length must hence be no greater than 16). +""" function hex2num(s::AbstractString) l = length(s) - l > 16 && throw(ArgumentError("the passed string must be of length <= 16, got $l")) + l > 16 && throw(ArgumentError("the length of the passed string must be <= 16, got $l")) hex2num(l <= 4 ? Float16 : l <= 8 ? Float32 : Float64, s) end diff --git a/base/int.jl b/base/int.jl index c568335b9f615..8e173ae06535a 100644 --- a/base/int.jl +++ b/base/int.jl @@ -29,7 +29,13 @@ const BitReal_types = (BitInteger_types..., BitFloat_types...) const BitReal = Union{BitReal_types...} reinterpret(::Type{Unsigned}, x::BitInteger) = unsigned(x) +reinterpret(::Type{ Signed}, x::BitInteger) = signed(x) +unsigned(::Type{T}) where {T<:Unsigned} = T +signed( ::Type{T}) where {T<:Signed} = T + +unsigned(::Type{<:Union{Bool,BitFloat}}) = UInt +signed( ::Type{<:Union{Bool,BitFloat}}) = Int ## integer comparisons ## diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 7fd538a9d9986..43e64270c6092 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -1,5 +1,25 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +# this construction is not available when put in int.jl and running in Core +for (S,U,F) in zip(BitSigned_types, BitUnsigned_types, + (nothing, Float16, Float32, Float64, nothing)) + @eval begin + unsigned(::Type{$S}) = $U + unsigned(::Type{$U}) = $U + signed( ::Type{$S}) = $S + signed( ::Type{$U}) = $S + reinterpret(::Type{Unsigned}, ::Type{$S}) = $U + reinterpret(::Type{Unsigned}, ::Type{$U}) = $U + reinterpret(::Type{Signed}, ::Type{$S}) = $S + reinterpret(::Type{Signed}, ::Type{$U}) = $S + end + F === nothing && continue + @eval begin + reinterpret(::Type{Unsigned}, ::Type{$F}) = $U + reinterpret(::Type{Signed}, ::Type{$F}) = $S + end + end + ## number-theoretic functions ## """ @@ -488,7 +508,7 @@ end An hexadecimal string of the binary representation of a number. See also the [`bits`](@ref) function, which is similar but gives -a binary string. +a binary string, and [`hex2num`] which does the opposite conversion. ```jldoctest julia> num2hex(Int64(4)) @@ -515,16 +535,9 @@ julia> hex2num(Float64, "400199999999999a") julia> hex2num(Int32, "fffffffe") -2 ``` - - hex2num(str) - -Convert a hexadecimal string to the floating point number it represents. -This function, which is inherently type-unstable, returns a `Float16`, -a `Float32`, or a `Float64` depending on the length of the string `str` -(note that this length must hence be no greater than 16). """ hex2num(::Type{T}, s::AbstractString) where {T<:BitReal} = - reinterpret(T, parse(unsigned(T), s, 16)) + reinterpret(T, parse(reinterpret(Unsigned, T), s, 16)) const base36digits = ['0':'9';'a':'z'] const base62digits = ['0':'9';'A':'Z';'a':'z'] diff --git a/base/multinverses.jl b/base/multinverses.jl index be34348a1a17e..88a0736651042 100644 --- a/base/multinverses.jl +++ b/base/multinverses.jl @@ -2,20 +2,10 @@ module MultiplicativeInverses -import Base: div, divrem, rem, unsigned +import Base: div, divrem, rem using Base: IndexLinear, IndexCartesian, tail export multiplicativeinverse -unsigned(::Type{Int8}) = UInt8 -unsigned(::Type{Int16}) = UInt16 -unsigned(::Type{Int32}) = UInt32 -unsigned(::Type{Int64}) = UInt64 -unsigned(::Type{Int128}) = UInt128 -unsigned(::Type{T}) where {T<:Unsigned} = T -unsigned(::Type{Float16}) = UInt16 -unsigned(::Type{Float32}) = UInt32 -unsigned(::Type{Float64}) = UInt64 - abstract type MultiplicativeInverse{T} end # Computes integer division by a constant using multiply, add, and bitshift. diff --git a/test/floatfuncs.jl b/test/floatfuncs.jl index 96ae477f0fdbd..7523355095d77 100644 --- a/test/floatfuncs.jl +++ b/test/floatfuncs.jl @@ -41,7 +41,7 @@ for elty in (Float16,Float32,Float64), _ = 1:10 @test hex2num(num2hex(x)) ≈ x @test hex2num(elty, num2hex(x)) ≈ x end -@test_throws ArgumentError hex2num(String(rand('a':'f', rand(17:100)))) +@test_throws ArgumentError hex2num(String(rand(['a':'f';'0':'1'], rand(17:100)))) # round for elty in (Float32,Float64) diff --git a/test/int.jl b/test/int.jl index 4108960fc6fc7..f902d10a4f4b9 100644 --- a/test/int.jl +++ b/test/int.jl @@ -1,7 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license # Test integer conversion routines from int.jl - +using Base: BitInteger_types, BitFloat_types for y in (-4, Float32(-4), -4.0, big(-4.0)) @test flipsign(3, y) == -3 @@ -18,10 +18,10 @@ for y in (4, Float32(4), 4.0, big(4.0)) end # Result type must be type of first argument, except for Bool -for U in (Base.BitInteger_types..., BigInt, +for U in (BitInteger_types..., BigInt, Rational{Int}, Rational{BigInt}, Float16, Float32, Float64) - for T in (Base.BitInteger_types..., BigInt, + for T in (BitInteger_types..., BigInt, Rational{Int}, Rational{BigInt}, Float16, Float32, Float64) @test typeof(copysign(T(3), U(4))) === T @@ -139,7 +139,7 @@ for T in (UInt8, UInt16, UInt32, UInt64) end # Test bit shifts -for T in Base.BitInteger_types +for T in BitInteger_types nbits = 8*sizeof(T) issigned = typemin(T) < 0 highbit = T(2) ^ (nbits-1) @@ -218,7 +218,34 @@ end @test unsafe_trunc(Int8, -129) === Int8(127) # Test x % T returns a T -for T in [Base.BitInteger_types..., BigInt], - U in [Base.BitInteger_types..., BigInt] +for T in [BitInteger_types..., BigInt], + U in [BitInteger_types..., BigInt] @test typeof(rand(U(0):U(127)) % T) === T end + +@testset "reinterpret and unsigned/signed on $T" for T in BitInteger_types + t = -1 % T + A, B = T <: Unsigned ? (Unsigned, Signed) : (Signed, Unsigned) + @test reinterpret(A, reinterpret(B, t)) == t + @test reinterpret(A, t) == t + @test reinterpret(B, t) != t + @test (T <: Unsigned ? unsigned : signed)(T) === T + @test unsigned(T) <: Unsigned + @test signed(T) <: Signed +end + +@testset "unsigned/signed(::Bool)" begin + @test signed(true) === 1 + @test signed(false) === 0 + @test unsigned(true) === 1 % UInt + @test unsigned(false) === 0 % UInt + @test signed(Bool) === Int + @test unsigned(Bool) === UInt +end + +@testset "unsigned/signed(::$T)" for T in BitFloat_types + @test signed(T(1)) === Int(1) + @test unsigned(T(1)) === UInt(1) + @test_throws InexactError signed(T(1.1)) + @test_throws InexactError unsigned(T(1.1)) +end From 3e066cb4b6ebe042e82e3208f150dcb0540f3a6e Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Fri, 26 May 2017 13:10:17 +0200 Subject: [PATCH 3/5] fix docstrings, indentations, ..., from reviews --- base/essentials.jl | 15 +-------------- base/int.jl | 25 ++++++++++++++++++++++++- base/intfuncs.jl | 39 +++++++++++++++++++-------------------- 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/base/essentials.jl b/base/essentials.jl index 20694b2027d80..c02a3a75c0ffd 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -145,17 +145,10 @@ Convert a number to an unsigned integer. If the argument is signed, it is reinte unsigned without checking for negative values. See also [`signed`](@ref). - unsigned(T::Type) -> UnsignedType - -Return the return-type of `unsigned(x::T)`, so that `unsigned(x)::unsigned(typeof(x))`. - ```jldoctest julia> unsigned(12) 0x000000000000000c -julia> unsigned(Int64) -UInt64 - julia> unsigned(2.0) 0x0000000000000002 @@ -173,23 +166,17 @@ Convert a number to a signed integer. If the argument is unsigned, it is reinter signed without checking for overflow. See also [`unsigned`](@ref). - signed(T::Type) -> SignedType - -Return the return-type of `signed(x::T)`, so that `signed(x)::signed(typeof(x))`. - ```jldoctest julia> signed(0xc) 12 -julia> signed(UInt64) -Int64 - julia> signed(2.0) 2 julia> signed(2.2) ERROR: InexactError() [...] +``` """ signed(x::UInt) = reinterpret(Int, x) diff --git a/base/int.jl b/base/int.jl index 8e173ae06535a..17dcfe259410b 100644 --- a/base/int.jl +++ b/base/int.jl @@ -28,11 +28,34 @@ const BitFloat = Union{BitFloat_types...} const BitReal_types = (BitInteger_types..., BitFloat_types...) const BitReal = Union{BitReal_types...} +## integer signed-ness conversions + reinterpret(::Type{Unsigned}, x::BitInteger) = unsigned(x) reinterpret(::Type{ Signed}, x::BitInteger) = signed(x) +""" + unsigned(T::Type) -> UnsignedType + +Return the return-type of `unsigned(x::T)`, so that `unsigned(x)::unsigned(typeof(x))`. + +```jldoctest +julia> unsigned(Int64) +UInt64 +``` +""" unsigned(::Type{T}) where {T<:Unsigned} = T -signed( ::Type{T}) where {T<:Signed} = T + +""" + signed(T::Type) -> SignedType + +Return the return-type of `signed(x::T)`, so that `signed(x)::signed(typeof(x))`. + +```jldoctest +julia> signed(UInt64) +Int64 +``` +""" +signed(::Type{T}) where {T<:Signed} = T unsigned(::Type{<:Union{Bool,BitFloat}}) = UInt signed( ::Type{<:Union{Bool,BitFloat}}) = Int diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 43e64270c6092..aa5228b298882 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -2,23 +2,23 @@ # this construction is not available when put in int.jl and running in Core for (S,U,F) in zip(BitSigned_types, BitUnsigned_types, - (nothing, Float16, Float32, Float64, nothing)) - @eval begin - unsigned(::Type{$S}) = $U - unsigned(::Type{$U}) = $U - signed( ::Type{$S}) = $S - signed( ::Type{$U}) = $S - reinterpret(::Type{Unsigned}, ::Type{$S}) = $U - reinterpret(::Type{Unsigned}, ::Type{$U}) = $U - reinterpret(::Type{Signed}, ::Type{$S}) = $S - reinterpret(::Type{Signed}, ::Type{$U}) = $S - end - F === nothing && continue - @eval begin - reinterpret(::Type{Unsigned}, ::Type{$F}) = $U - reinterpret(::Type{Signed}, ::Type{$F}) = $S - end + (nothing, Float16, Float32, Float64, nothing)) + @eval begin + unsigned(::Type{$S}) = $U + unsigned(::Type{$U}) = $U + signed( ::Type{$S}) = $S + signed( ::Type{$U}) = $S + reinterpret(::Type{Unsigned}, ::Type{$S}) = $U + reinterpret(::Type{Unsigned}, ::Type{$U}) = $U + reinterpret(::Type{Signed}, ::Type{$S}) = $S + reinterpret(::Type{Signed}, ::Type{$U}) = $S end + F === nothing && continue + @eval begin + reinterpret(::Type{Unsigned}, ::Type{$F}) = $U + reinterpret(::Type{Signed}, ::Type{$F}) = $S + end +end ## number-theoretic functions ## @@ -506,9 +506,9 @@ end """ num2hex(f) -An hexadecimal string of the binary representation of a number. +A hexadecimal string of the binary representation of a number. See also the [`bits`](@ref) function, which is similar but gives -a binary string, and [`hex2num`] which does the opposite conversion. +a binary string, and [`hex2num`](@ref) which does the opposite conversion. ```jldoctest julia> num2hex(Int64(4)) @@ -516,7 +516,6 @@ julia> num2hex(Int64(4)) julia> num2hex(2.2) "400199999999999a" - ``` """ num2hex(n::BitReal) = hex(reinterpret(Unsigned, n), sizeof(n)*2) @@ -632,7 +631,7 @@ dec A string giving the literal bit representation of a number. See also the [`num2hex`](@ref) function, which is similar but -gives an hexadecimal string. +gives a hexadecimal string. ```jldoctest julia> bits(4) From 4908f49e4ea0571f2a4fe5ed4995cef4354c6c2c Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sat, 3 Jun 2017 13:06:49 +0200 Subject: [PATCH 4/5] fix Bool case --- base/int.jl | 4 ++++ base/intfuncs.jl | 5 ++--- test/intfuncs.jl | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/base/int.jl b/base/int.jl index 17dcfe259410b..e9ed9cc70fb65 100644 --- a/base/int.jl +++ b/base/int.jl @@ -32,6 +32,10 @@ const BitReal = Union{BitReal_types...} reinterpret(::Type{Unsigned}, x::BitInteger) = unsigned(x) reinterpret(::Type{ Signed}, x::BitInteger) = signed(x) +reinterpret(::Type{Unsigned}, ::Type{Bool}) = UInt8 +reinterpret(::Type{ Signed}, ::Type{Bool}) = Int8 +reinterpret(::Type{Unsigned}, x::Bool) = x % UInt8 +reinterpret(::Type{ Signed}, x::Bool) = x % Int8 """ unsigned(T::Type) -> UnsignedType diff --git a/base/intfuncs.jl b/base/intfuncs.jl index aa5228b298882..2a078d68eaa72 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -518,8 +518,7 @@ julia> num2hex(2.2) "400199999999999a" ``` """ -num2hex(n::BitReal) = hex(reinterpret(Unsigned, n), sizeof(n)*2) -num2hex(n::Bool) = hex(n) +num2hex(n::Union{Bool,BitReal}) = hex(reinterpret(Unsigned, n), sizeof(n)*2) """ hex2num(T::Type, str) @@ -535,7 +534,7 @@ julia> hex2num(Int32, "fffffffe") -2 ``` """ -hex2num(::Type{T}, s::AbstractString) where {T<:BitReal} = +hex2num(::Type{T}, s::AbstractString) where {T<:Union{Bool,BitReal}} = reinterpret(T, parse(reinterpret(Unsigned, T), s, 16)) const base36digits = ['0':'9';'a':'z'] diff --git a/test/intfuncs.jl b/test/intfuncs.jl index 175c0fe5dbbf5..445670ec775a5 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -125,8 +125,8 @@ end @test hex(-12, 3) == "-00c" @test num2hex(1243) == (Int == Int32 ? "000004db" : "00000000000004db") @test num2hex(-1243) == (Int == Int32 ? "fffffb25" : "fffffffffffffb25") -@test num2hex(true) == "1" -@test num2hex(false) == "0" +@test num2hex(true) == "01" +@test num2hex(false) == "00" for elty in Base.BitInteger_types, _ = 1:10 x = rand(elty) From d7771bf0124f36cd75fdd614f7f24eb90444223b Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sat, 3 Jun 2017 17:49:54 +0200 Subject: [PATCH 5/5] deprecate hex2num (=> reinterpret) & num2hex (=> bits) --- base/deprecated.jl | 19 +++++++++++++++ base/exports.jl | 2 -- base/floatfuncs.jl | 14 ----------- base/int.jl | 4 +++ base/intfuncs.jl | 51 +++++++++++++++------------------------ doc/src/stdlib/numbers.md | 2 -- test/floatfuncs.jl | 6 ++--- test/intfuncs.jl | 10 ++++---- test/numbers.jl | 26 ++++++++++---------- 9 files changed, 62 insertions(+), 72 deletions(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index 66d4af48e4cf8..2a1b1088566f1 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1350,6 +1350,25 @@ end @deprecate srand(filename::AbstractString, n::Integer=4) srand(read!(filename, Array{UInt32}(Int(n)))) @deprecate MersenneTwister(filename::AbstractString) srand(MersenneTwister(0), read!(filename, Array{UInt32}(Int(4)))) +# PR #22203 +@deprecate num2hex(n::Union{Bool, Base.BitReal}) bits(n, hex) + +""" + hex2num(str) + +Convert a hexadecimal string to the floating point number it represents. +This function, which is inherently type-unstable, returns a `Float16`, +a `Float32`, or a `Float64` depending on the length of the string `str` +(note that this length must hence be no greater than 16). +""" +function hex2num(s::AbstractString) + depwarn("hex2num(s) is deprecated, use reinterpret(T::Type, s)", :hex2num) + l = length(s) + l > 16 && throw(ArgumentError("the length of the passed string must be <= 16, got $l")) + reinterpret(l <= 4 ? Float16 : l <= 8 ? Float32 : Float64, s) +end +export hex2num + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/exports.jl b/base/exports.jl index b9bd9ecebe427..a76072fc17984 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -340,7 +340,6 @@ export gamma, gcd, gcdx, - hex2num, hypot, imag, inv, @@ -378,7 +377,6 @@ export nextpow2, nextprod, numerator, - num2hex, one, oneunit, powermod, diff --git a/base/floatfuncs.jl b/base/floatfuncs.jl index 50dcfb44b32e2..904f9088336e9 100644 --- a/base/floatfuncs.jl +++ b/base/floatfuncs.jl @@ -29,20 +29,6 @@ maxintfloat() = maxintfloat(Float64) isinteger(x::AbstractFloat) = (x - trunc(x) == 0) -""" - hex2num(str) - -Convert a hexadecimal string to the floating point number it represents. -This function, which is inherently type-unstable, returns a `Float16`, -a `Float32`, or a `Float64` depending on the length of the string `str` -(note that this length must hence be no greater than 16). -""" -function hex2num(s::AbstractString) - l = length(s) - l > 16 && throw(ArgumentError("the length of the passed string must be <= 16, got $l")) - hex2num(l <= 4 ? Float16 : l <= 8 ? Float32 : Float64, s) -end - """ round([T,] x, [digits, [base]], [r::RoundingMode]) diff --git a/base/int.jl b/base/int.jl index e9ed9cc70fb65..3eaede6288e58 100644 --- a/base/int.jl +++ b/base/int.jl @@ -36,6 +36,10 @@ reinterpret(::Type{Unsigned}, ::Type{Bool}) = UInt8 reinterpret(::Type{ Signed}, ::Type{Bool}) = Int8 reinterpret(::Type{Unsigned}, x::Bool) = x % UInt8 reinterpret(::Type{ Signed}, x::Bool) = x % Int8 +reinterpret(::Type{Unsigned}, ::Type{Char}) = UInt32 +reinterpret(::Type{ Signed}, ::Type{Char}) = Int32 +reinterpret(::Type{Unsigned}, x::Char) = unsigned(x) +reinterpret(::Type{ Signed}, x::Char) = signed(x) """ unsigned(T::Type) -> UnsignedType diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 2a078d68eaa72..ae73562b08a4b 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -504,38 +504,21 @@ function hex(x::Unsigned, pad::Int, neg::Bool) end """ - num2hex(f) + reinterpret(T::Type, str::AbstractString, b=16) -A hexadecimal string of the binary representation of a number. -See also the [`bits`](@ref) function, which is similar but gives -a binary string, and [`hex2num`](@ref) which does the opposite conversion. +Interprets a string as the bit representation of a +number of type `T`, encoded in base `b`. ```jldoctest -julia> num2hex(Int64(4)) -"0000000000000004" - -julia> num2hex(2.2) -"400199999999999a" -``` -""" -num2hex(n::Union{Bool,BitReal}) = hex(reinterpret(Unsigned, n), sizeof(n)*2) - -""" - hex2num(T::Type, str) - -Interprets a hexadecimal string as the bit representation of a -number of type `T`. See also [`num2hex`](@ref). - -```jldoctest -julia> hex2num(Float64, "400199999999999a") +julia> reinterpret(Float64, "400199999999999a") 2.2 -julia> hex2num(Int32, "fffffffe") +julia> reinterpret(Int32, "fffffffe") -2 ``` """ -hex2num(::Type{T}, s::AbstractString) where {T<:Union{Bool,BitReal}} = - reinterpret(T, parse(reinterpret(Unsigned, T), s, 16)) +reinterpret(::Type{T}, s::AbstractString, b=16) where {T<:Union{Bool,Char,BitReal}} = + reinterpret(T, parse(reinterpret(Unsigned, T), s, b)) const base36digits = ['0':'9';'a':'z'] const base62digits = ['0':'9';'A':'Z';'a':'z'] @@ -626,25 +609,29 @@ Convert an integer to a decimal string, optionally specifying a number of digits dec """ - bits(n) + bits(n, fmt=bin) A string giving the literal bit representation of a number. -See also the [`num2hex`](@ref) function, which is similar but -gives a hexadecimal string. +The `fmt` function, which can be `bin` or `hex`, +determines the format used to represent those bits +respectively as a binary or hexadecimal string. ```jldoctest julia> bits(4) "0000000000000000000000000000000000000000000000000000000000000100" +julia> bits(4, hex) +"0000000000000004" + julia> bits(2.2) "0100000000000001100110011001100110011001100110011001100110011010" + +julia> bits(2.2, hex) +"400199999999999a" ``` """ -bits(x::Union{Bool,Int8,UInt8}) = bin(reinterpret(UInt8,x),8) -bits(x::Union{Int16,UInt16,Float16}) = bin(reinterpret(UInt16,x),16) -bits(x::Union{Char,Int32,UInt32,Float32}) = bin(reinterpret(UInt32,x),32) -bits(x::Union{Int64,UInt64,Float64}) = bin(reinterpret(UInt64,x),64) -bits(x::Union{Int128,UInt128}) = bin(reinterpret(UInt128,x),128) +bits(n::Union{Bool,Char,BitReal}, fmt::Union{typeof(bin), typeof(hex)}=bin) = + fmt(reinterpret(Unsigned, n), sizeof(n)*(fmt === bin ? 8 : 2)) """ digits([T<:Integer], n::Integer, base::T=10, pad::Integer=1) diff --git a/doc/src/stdlib/numbers.md b/doc/src/stdlib/numbers.md index af00def34e64e..2c3d44920a8c9 100644 --- a/doc/src/stdlib/numbers.md +++ b/doc/src/stdlib/numbers.md @@ -58,8 +58,6 @@ Base.Math.significand Base.Math.exponent Base.complex(::Complex) Base.bswap -Base.num2hex -Base.hex2num Base.hex2bytes Base.bytes2hex ``` diff --git a/test/floatfuncs.jl b/test/floatfuncs.jl index 7523355095d77..e46a1c797c469 100644 --- a/test/floatfuncs.jl +++ b/test/floatfuncs.jl @@ -35,13 +35,11 @@ for elty in (Float16,Float32,Float64) @test !isinteger(elty(NaN)) end -# num2hex, hex2num +# bits, reinterpret for elty in (Float16,Float32,Float64), _ = 1:10 x = rand(elty) - @test hex2num(num2hex(x)) ≈ x - @test hex2num(elty, num2hex(x)) ≈ x + @test reinterpret(elty, bits(x, hex)) ≈ x end -@test_throws ArgumentError hex2num(String(rand(['a':'f';'0':'1'], rand(17:100)))) # round for elty in (Float32,Float64) diff --git a/test/intfuncs.jl b/test/intfuncs.jl index 445670ec775a5..815a773849816 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -123,14 +123,14 @@ end @test hex(12) == "c" @test hex(-12, 3) == "-00c" -@test num2hex(1243) == (Int == Int32 ? "000004db" : "00000000000004db") -@test num2hex(-1243) == (Int == Int32 ? "fffffb25" : "fffffffffffffb25") -@test num2hex(true) == "01" -@test num2hex(false) == "00" +@test bits(1243, hex) == (Int == Int32 ? "000004db" : "00000000000004db") +@test bits(-1243, hex) == (Int == Int32 ? "fffffb25" : "fffffffffffffb25") +@test bits(true, hex) == "01" +@test bits(false, hex) == "00" for elty in Base.BitInteger_types, _ = 1:10 x = rand(elty) - @test hex2num(elty, num2hex(x)) == x + @test reinterpret(elty, bits(x, hex)) == x end @test base(2, 5, 7) == "0000101" diff --git a/test/numbers.jl b/test/numbers.jl index 24fcb24a1864a..d9735e2935241 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -386,19 +386,19 @@ end @test base(12,typemin(Int128)) == "-2a695925806818735399a37a20a31b3534a8" @test base(12,typemax(Int128)) == "2a695925806818735399a37a20a31b3534a7" -@test hex2num("3ff0000000000000") == 1. -@test hex2num("bff0000000000000") == -1. -@test hex2num("4000000000000000") == 2. -@test hex2num("7ff0000000000000") == Inf -@test hex2num("fff0000000000000") == -Inf -@test isnan(hex2num("7ff8000000000000")) -@test isnan(hex2num("fff8000000000000")) -@test hex2num("3f800000") == 1.0f0 -@test hex2num("bf800000") == -1.0f0 -@test hex2num("7f800000") == Inf32 -@test hex2num("ff800000") == -Inf32 -@test isnan(hex2num("7fc00000")) -@test isnan(hex2num("ffc00000")) +@test reinterpret(Float64, "3ff0000000000000") == 1. +@test reinterpret(Float64, "bff0000000000000") == -1. +@test reinterpret(Float64, "4000000000000000") == 2. +@test reinterpret(Float64, "7ff0000000000000") == Inf +@test reinterpret(Float64, "fff0000000000000") == -Inf +@test isnan(reinterpret(Float64, "7ff8000000000000")) +@test isnan(reinterpret(Float64, "fff8000000000000")) +@test reinterpret(Float32, "3f800000") == 1.0f0 +@test reinterpret(Float32, "bf800000") == -1.0f0 +@test reinterpret(Float32, "7f800000") == Inf32 +@test reinterpret(Float32, "ff800000") == -Inf32 +@test isnan(reinterpret(Float32, "7fc00000")) +@test isnan(reinterpret(Float32, "ffc00000")) # floating-point printing @test repr(1.0) == "1.0"