-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
exact BigFloat
to IEEE FP conversion in pure Julia
#50691
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -137,6 +137,68 @@ i.e. the maximum integer value representable by [`exponent_bits(T)`](@ref) bits. | |
""" | ||
function exponent_raw_max end | ||
|
||
""" | ||
IEEE 754 definition of the minimum exponent. | ||
""" | ||
ieee754_exponent_min(::Type{T}) where {T<:IEEEFloat} = Int(1 - exponent_max(T))::Int | ||
|
||
exponent_min(::Type{Float16}) = ieee754_exponent_min(Float16) | ||
exponent_min(::Type{Float32}) = ieee754_exponent_min(Float32) | ||
exponent_min(::Type{Float64}) = ieee754_exponent_min(Float64) | ||
|
||
function ieee754_representation( | ||
::Type{F}, sign_bit::Bool, exponent_field::Integer, significand_field::Integer | ||
) where {F<:IEEEFloat} | ||
T = uinttype(F) | ||
ret::T = sign_bit | ||
ret <<= exponent_bits(F) | ||
ret |= exponent_field | ||
ret <<= significand_bits(F) | ||
ret |= significand_field | ||
end | ||
|
||
# ±floatmax(T) | ||
function ieee754_representation( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do any of these functions need to exist? Why are they better than There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The difference is they give the representation of a floating-point number as an unsigned integer. In mpfr.jl, in Apart from that, these additions to float.jl could actually be used (in a future PR) for refactoring float.jl so as to remove almost all "magic constants" from the file. So, taking |
||
::Type{F}, sign_bit::Bool, ::Val{:omega} | ||
) where {F<:IEEEFloat} | ||
ieee754_representation(F, sign_bit, exponent_raw_max(F) - 1, significand_mask(F)) | ||
end | ||
|
||
# NaN or an infinity | ||
function ieee754_representation( | ||
::Type{F}, sign_bit::Bool, significand_field::Integer, ::Val{:nan} | ||
) where {F<:IEEEFloat} | ||
ieee754_representation(F, sign_bit, exponent_raw_max(F), significand_field) | ||
end | ||
|
||
# NaN with default payload | ||
function ieee754_representation( | ||
::Type{F}, sign_bit::Bool, ::Val{:nan} | ||
) where {F<:IEEEFloat} | ||
ieee754_representation(F, sign_bit, one(uinttype(F)) << (significand_bits(F) - 1), Val(:nan)) | ||
end | ||
|
||
# Infinity | ||
function ieee754_representation( | ||
::Type{F}, sign_bit::Bool, ::Val{:inf} | ||
) where {F<:IEEEFloat} | ||
ieee754_representation(F, sign_bit, false, Val(:nan)) | ||
end | ||
|
||
# Subnormal or zero | ||
function ieee754_representation( | ||
::Type{F}, sign_bit::Bool, significand_field::Integer, ::Val{:subnormal} | ||
) where {F<:IEEEFloat} | ||
ieee754_representation(F, sign_bit, false, significand_field) | ||
end | ||
|
||
# Zero | ||
function ieee754_representation( | ||
::Type{F}, sign_bit::Bool, ::Val{:zero} | ||
) where {F<:IEEEFloat} | ||
ieee754_representation(F, sign_bit, false, Val(:subnormal)) | ||
end | ||
|
||
""" | ||
uabs(x::Integer) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
# This file is a part of Julia. License is MIT: https://julialang.org/license | ||
|
||
""" | ||
Segment of raw words of bits interpreted as a big integer. Less | ||
significant words come first. Each word is in machine-native bit-order. | ||
""" | ||
struct RawBigInt{T<:Unsigned} | ||
d::Ptr{T} | ||
word_count::Int | ||
|
||
function RawBigInt{T}(d::Ptr{T}, word_count::Int) where {T<:Unsigned} | ||
new{T}(d, word_count) | ||
end | ||
end | ||
|
||
RawBigInt(d::Ptr{T}, word_count::Int) where {T<:Unsigned} = RawBigInt{T}(d, word_count) | ||
elem_count(x::RawBigInt, ::Val{:words}) = x.word_count | ||
elem_count(x::Unsigned, ::Val{:bits}) = sizeof(x) * 8 | ||
word_length(::RawBigInt{T}) where {T} = elem_count(zero(T), Val(:bits)) | ||
elem_count(x::RawBigInt{T}, ::Val{:bits}) where {T} = word_length(x) * elem_count(x, Val(:words)) | ||
reversed_index(n::Int, i::Int) = n - i - 1 | ||
reversed_index(x, i::Int, v::Val) = reversed_index(elem_count(x, v), i)::Int | ||
split_bit_index(x::RawBigInt, i::Int) = divrem(i, word_length(x), RoundToZero) | ||
|
||
""" | ||
`i` is the zero-based index of the wanted word in `x`, starting from | ||
the less significant words. | ||
""" | ||
function get_elem(x::RawBigInt, i::Int, ::Val{:words}, ::Val{:ascending}) | ||
unsafe_load(x.d, i + 1) | ||
end | ||
|
||
function get_elem(x, i::Int, v::Val, ::Val{:descending}) | ||
j = reversed_index(x, i, v) | ||
get_elem(x, j, v, Val(:ascending)) | ||
end | ||
|
||
word_is_nonzero(x::RawBigInt, i::Int, v::Val) = !iszero(get_elem(x, i, Val(:words), v)) | ||
|
||
word_is_nonzero(x::RawBigInt, v::Val) = let x = x | ||
i -> word_is_nonzero(x, i, v) | ||
end | ||
|
||
""" | ||
Returns a `Bool` indicating whether the `len` least significant words | ||
of `x` are nonzero. | ||
""" | ||
function tail_is_nonzero(x::RawBigInt, len::Int, ::Val{:words}) | ||
any(word_is_nonzero(x, Val(:ascending)), 0:(len - 1)) | ||
end | ||
|
||
""" | ||
Returns a `Bool` indicating whether the `len` least significant bits of | ||
the `i`-th (zero-based index) word of `x` are nonzero. | ||
""" | ||
function tail_is_nonzero(x::RawBigInt, len::Int, i::Int, ::Val{:word}) | ||
!iszero(len) && | ||
!iszero(get_elem(x, i, Val(:words), Val(:ascending)) << (word_length(x) - len)) | ||
end | ||
|
||
""" | ||
Returns a `Bool` indicating whether the `len` least significant bits of | ||
`x` are nonzero. | ||
""" | ||
function tail_is_nonzero(x::RawBigInt, len::Int, ::Val{:bits}) | ||
if 0 < len | ||
word_count, bit_count_in_word = split_bit_index(x, len) | ||
tail_is_nonzero(x, bit_count_in_word, word_count, Val(:word)) || | ||
tail_is_nonzero(x, word_count, Val(:words)) | ||
else | ||
false | ||
end::Bool | ||
end | ||
|
||
""" | ||
Returns a `Bool` that is the `i`-th (zero-based index) bit of `x`. | ||
""" | ||
function get_elem(x::Unsigned, i::Int, ::Val{:bits}, ::Val{:ascending}) | ||
(x >>> i) % Bool | ||
end | ||
|
||
""" | ||
Returns a `Bool` that is the `i`-th (zero-based index) bit of `x`. | ||
""" | ||
function get_elem(x::RawBigInt, i::Int, ::Val{:bits}, v::Val{:ascending}) | ||
vb = Val(:bits) | ||
if 0 ≤ i < elem_count(x, vb) | ||
word_index, bit_index_in_word = split_bit_index(x, i) | ||
word = get_elem(x, word_index, Val(:words), v) | ||
get_elem(word, bit_index_in_word, vb, v) | ||
else | ||
false | ||
end::Bool | ||
end | ||
|
||
""" | ||
Returns an integer of type `R`, consisting of the `len` most | ||
significant bits of `x`. | ||
""" | ||
function truncated(::Type{R}, x::RawBigInt, len::Int) where {R<:Integer} | ||
ret = zero(R) | ||
if 0 < len | ||
word_count, bit_count_in_word = split_bit_index(x, len) | ||
k = word_length(x) | ||
vals = (Val(:words), Val(:descending)) | ||
|
||
for w ∈ 0:(word_count - 1) | ||
ret <<= k | ||
word = get_elem(x, w, vals...) | ||
ret |= R(word) | ||
end | ||
|
||
if !iszero(bit_count_in_word) | ||
ret <<= bit_count_in_word | ||
wrd = get_elem(x, word_count, vals...) | ||
ret |= R(wrd >>> (k - bit_count_in_word)) | ||
end | ||
end | ||
ret::R | ||
end | ||
|
||
struct RawBigIntRoundingIncrementHelper{T<:Unsigned} | ||
n::RawBigInt{T} | ||
trunc_len::Int | ||
|
||
final_bit::Bool | ||
round_bit::Bool | ||
|
||
function RawBigIntRoundingIncrementHelper{T}(n::RawBigInt{T}, len::Int) where {T<:Unsigned} | ||
vals = (Val(:bits), Val(:descending)) | ||
f = get_elem(n, len - 1, vals...) | ||
r = get_elem(n, len , vals...) | ||
new{T}(n, len, f, r) | ||
end | ||
end | ||
|
||
function RawBigIntRoundingIncrementHelper(n::RawBigInt{T}, len::Int) where {T<:Unsigned} | ||
RawBigIntRoundingIncrementHelper{T}(n, len) | ||
end | ||
|
||
(h::RawBigIntRoundingIncrementHelper)(::Rounding.FinalBit) = h.final_bit | ||
|
||
(h::RawBigIntRoundingIncrementHelper)(::Rounding.RoundBit) = h.round_bit | ||
|
||
function (h::RawBigIntRoundingIncrementHelper)(::Rounding.StickyBit) | ||
v = Val(:bits) | ||
n = h.n | ||
tail_is_nonzero(n, elem_count(n, v) - h.trunc_len - 1, v) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do these functions need to exist?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean why is
exponent_min
necessary in addition toieee754_exponent_min
?The idea was to have
exponent_min
be consistent with functions likeexponent_max
, in that it only has methods that are strictly required. To be honest, I don't really understand the significance of that, I was just trying to follow the discussion in #50654, the "incorrectly switches the position / ordering ofUnion
andType
" part, in particular.Apart from that the formula in the definition of
ieee754_exponent_min
is defined in the IEEE 754 standard, and it's only known to be valid for IEEE 754 floating-point. So theoretically we could have some non-IEEE floating-point types with different definitions ofexponent_min
.