-
-
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
Conversion from Float64 to Float16 not always rounded to nearest #40315
Comments
Can you test the following to see if you can find any issues?
|
@oscardssmith I have not reviewed your code yet but I have tested it. It cures the problem shown in the example above. However, it fails more often than the current code in Julia 1.6.0 to round correctly from Float64 to Float16. Example: the conversion of Float64(51227.0) to a Float16 gives Float16(51232.0) with Julia 1.6.0 and Float16(51200.0) with the code above, which is patently wrong. |
I get julia> convert(Float16,63343.99805) == 63328.0
true
julia> convert(Float16,63343.99805) == 63360.0
false
julia> convert(Float16,63344.0) == 63360.0
true
julia> convert(Float16,prevfloat(63344.0)) == 63360.0
false on 1.6.0, which seems to be correct. Which also follows the tie-to-even rule as 63344 as the tie is round up, which is the even float julia> bitstring(Float16(63328))
"0111101110111011" # odd
julia> bitstring(Float16(63360))
"0111101110111100" # even |
@oscardssmith I believe julia> @code_llvm Float16(63344.0)
; @ float.jl:180 within `Float16'
define half @julia_Float16_185(double %0) {
top:
%1 = fptrunc double %0 to half
ret half %1
} However, in Julia 1.5.2 julia> @code_llvm Float16(63344.0)
; @ float.jl:252 within `Float16'
define i16 @julia_Float16_77(double) {
top:
; ┌ @ float.jl:251 within `Float32'
%1 = fptrunc double %0 to float
; └
; @ float.jl:252 within `Float16' @ float.jl:182
; ┌ @ essentials.jl:414 within `reinterpret'
%2 = bitcast float %1 to i32
; └
; @ float.jl:252 within `Float16' @ float.jl:183
; ┌ @ float.jl:536 within `isnan'
; │┌ @ float.jl:455 within `!='
%3 = fcmp ord float %1, 0.000000e+00
; └└
br i1 %3, label %L14, label %L5
L5: ; preds = %top
; @ float.jl:252 within `Float16' @ float.jl:184
; ┌ @ int.jl:455 within `>>'
%4 = lshr i32 %2, 16
... |
@milankl I stand by what I said previously. On Julia 1.6.1, I get: julia> convert(Float16,63343.99805) == 63328.0
false
julia> convert(Float16,63343.99805) == 63360.0
true
julia> convert(Float16,63344.0) == 63360.0
true
julia> convert(Float16,prevfloat(63344.0)) == 63360.0
true I cannot explain the difference with what you obtain, though. |
Just checked on 1.6.1 too, where I reproduce my results from 1.6.0 - as it should be. However, I see that with <1.6 the look-up table from |
@milankl Here you go: julia> @code_llvm Float16(63344.0)
; @ float.jl:180 within `Float16'
define half @julia_Float16_1735(double %0) {
top:
%1 = fptrunc double %0 to half
ret half %1
} Seems identical to your output. FWIW, I am running the 64 bits "Generic Linux on x86" version. |
Okay, I get the correct results on macOS (x86), linux (arm, A64FX), but can confirm that there's a bug on linux x86. |
You should look at Lines 1301 to 1493 in 9f32653
|
On 1.5.2, macOS, x86 I get (result is incorrect) julia> @code_native Float16(63343.99805)
.section __TEXT,__text,regular,pure_instructions
; ┌ @ float.jl:252 within `Float16'
; │┌ @ float.jl:251 within `Float32'
vcvtsd2ss %xmm0, %xmm0, %xmm0
; │└
; │ @ float.jl:252 within `Float16' @ float.jl:182
; │┌ @ essentials.jl:414 within `reinterpret'
vmovd %xmm0, %ecx
; │└
; │ @ float.jl:252 within `Float16' @ float.jl:183
; │┌ @ float.jl:536 within `isnan'
; ││┌ @ float.jl:455 within `!='
vucomiss %xmm0, %xmm0
; │└└
... On 1.6.1, macOS, x86 this is (result is correct) julia> @code_native Float16(63343.99805)
.section __TEXT,__text,regular,pure_instructions
; ┌ @ float.jl:180 within `Float16'
pushq %rax
movabsq $__truncdfhf2, %rax
callq *%rax
popq %rcx
retq
nop
; └ which looks similar to 1.6.0, linux, x86 (but result is incorrect) julia> @code_native Float16(63343.99805)
.text
; ┌ @ float.jl:180 within `Float16'
pushq %rax
movabsq $__truncdfhf2, %rax
callq *%rax
popq %rcx
retq
nop
; └
and this is 1.6.0, linux, arm, A64FX (i.e. with Float16 hardware, result is correct) julia> @code_native Float16(63343.99805)
.text
; ┌ @ float.jl:180 within `Float16'
fcvt h0, d0
ret
nop
nop
nop
nop
nop
nop
; └ @maleadt Does that make sense to you? Why would 1.6 on macOS produce correct results, but 1.5 doesn't? |
Probably some platform-dependent error in that C implementation of Lines 140 to 248 in 69fcb57
|
Numbers 63328.0 and 63360.0 are perfectly representable in the Float16 format (with nextfloat(Float16(63328.0)) == Float16(63360.0)). The middle of the interval [63328.0,63360.0] is 63344.0. All Float64 in [63344.0, 63360.0] should round to Float16(63360.0); all Float64 in [63328.0,63344.0) should round to Float16(63328.0).
Yet with Julia 1.6.0, we have:
julia> convert(Float16,63343.99805) == 63328.0
false
julia> convert(Float16,63343.99805) == 63360.0
true
The text was updated successfully, but these errors were encountered: