Skip to content

Commit 707c57c

Browse files
authored
fixed Float16 from Float64 and BigFloat (#42837)
* fixed Float16 from Float64 and BigFloat. Many thanks to Jamison.
1 parent ee36c13 commit 707c57c

File tree

3 files changed

+57
-3
lines changed

3 files changed

+57
-3
lines changed

base/mpfr.jl

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,8 +338,23 @@ Float32(x::BigFloat, r::MPFRRoundingMode=ROUNDING_MODE[]) =
338338
_cpynansgn(ccall((:mpfr_get_flt,:libmpfr), Float32, (Ref{BigFloat}, MPFRRoundingMode), x, r), x)
339339
Float32(x::BigFloat, r::RoundingMode) = Float32(x, convert(MPFRRoundingMode, r))
340340

341-
# TODO: avoid double rounding
342-
Float16(x::BigFloat) = Float16(Float64(x))
341+
function Float16(x::BigFloat) :: Float16
342+
res = Float32(x)
343+
resi = reinterpret(UInt32, res)
344+
if (resi&0x7fffffff) < 0x38800000 # if Float16(res) is subnormal
345+
#shift so that the mantissa lines up where it would for normal Float16
346+
shift = 113-((resi & 0x7f800000)>>23)
347+
if shift<23
348+
resi |= 0x0080_0000 # set implicit bit
349+
resi >>= shift
350+
end
351+
end
352+
if (resi & 0x1fff == 0x1000) # if we are halfway between 2 Float16 values
353+
# adjust the value by 1 ULP in the direction that will make Float16(res) give the right answer
354+
res = nextfloat(res, cmp(x, res))
355+
end
356+
return res
357+
end
343358

344359
promote_rule(::Type{BigFloat}, ::Type{<:Real}) = BigFloat
345360
promote_rule(::Type{BigInt}, ::Type{<:AbstractFloat}) = BigFloat

src/runtime_intrinsics.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,24 @@ JL_DLLEXPORT uint16_t __gnu_f2h_ieee(float param)
205205

206206
JL_DLLEXPORT uint16_t __truncdfhf2(double param)
207207
{
208-
return float_to_half((float)param);
208+
float res = (float)param;
209+
uint32_t resi;
210+
memcpy(&resi, &res, sizeof(res));
211+
if ((resi&0x7fffffffu) < 0x38800000u){ // if Float16(res) is subnormal
212+
// shift so that the mantissa lines up where it would for normal Float16
213+
uint32_t shift = 113u-((resi & 0x7f800000u)>>23u);
214+
if (shift<23u) {
215+
resi |= 0x00800000; // set implicit bit
216+
resi >>= shift;
217+
}
218+
}
219+
if ((resi & 0x1fffu) == 0x1000u) { // if we are halfway between 2 Float16 values
220+
memcpy(&resi, &res, sizeof(res));
221+
// adjust the value by 1 ULP in the direction that will make Float16(res) give the right answer
222+
resi += (fabs(res) < fabs(param)) - (fabs(param) < fabs(res));
223+
memcpy(&res, &resi, sizeof(res));
224+
}
225+
return float_to_half(res);
209226
}
210227

211228
#endif

test/float16.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,25 @@ const minsubf16_32 = Float32(minsubf16)
202202

203203
# issues #33076
204204
@test Float16(1f5) == Inf16
205+
206+
@testset "conversion to Float16 from" begin
207+
for T in (Float32, Float64, BigFloat)
208+
@testset "conversion from $T" begin
209+
for i in 1:2^16
210+
f = reinterpret(Float16, UInt16(i-1))
211+
isfinite(f) || continue
212+
if f < 0
213+
epsdown = T(eps(f))/2
214+
epsup = issubnormal(f) ? epsdown : T(eps(nextfloat(f)))/2
215+
else
216+
epsup = T(eps(f))/2
217+
epsdown = issubnormal(f) ? epsup : T(eps(prevfloat(f)))/2
218+
end
219+
@test isequal(f*(-1)^(f === Float16(0)), Float16(nextfloat(T(f) - epsdown)))
220+
@test isequal(f*(-1)^(f === -Float16(0)), Float16(prevfloat(T(f) + epsup)))
221+
@test isequal(prevfloat(f), Float16(prevfloat(T(f) - epsdown)))
222+
@test isequal(nextfloat(f), Float16(nextfloat(T(f) + epsup)))
223+
end
224+
end
225+
end
226+
end

0 commit comments

Comments
 (0)