Skip to content

Commit f5668d6

Browse files
authored
more accurate rad2deg and deg2rad for Float16 and Float32 (#59097)
Both `deg2rad(::AbstractFloat)` and `rad2deg(::AbstractFloat)` just multiply the input argument with a constant-foldable factor of the same type as of the input argument. The generic code for computing the factor involves a double rounding, so the factor is not guaranteed to be correctly rounded. Introduce special cases for `Float16` and `Float32` to make the factor correctly rounded. This is not necessary for `Float64`, as the factor happens to be correctly rounded in that case. Direct improvements: * improve accuracy of `deg2rad(::Float16)` * improve accuracy of `rad2deg(::Float16)` * improve accuracy of `rad2deg(::Float32)` Example indirect improvements: * improving accuracy of `rad2deg(::Float32)` also improves accuracy of `asind(::Float32)`, `acosd(::Float32)`, `atand(::Float32)`
1 parent f9b7d27 commit f5668d6

File tree

2 files changed

+34
-3
lines changed

2 files changed

+34
-3
lines changed

base/math.jl

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,27 @@ end
228228
return hi, lo
229229
end
230230

231+
# generic, but involves double rounding
232+
function _180_over_pi(z::AbstractFloat)
233+
180 / oftype(z, pi)
234+
end
235+
function _pi_over_180(z::AbstractFloat)
236+
oftype(z, pi) / 180
237+
end
238+
239+
# rounded to closest representable number where necessary
240+
function _180_over_pi(z::Union{Float16, Float32})
241+
if z isa Float16
242+
r = Float16(57.28)
243+
elseif z isa Float32
244+
r = 57.29578f0
245+
end
246+
r
247+
end
248+
function _pi_over_180(::Float16)
249+
Float16(0.01746)
250+
end
251+
231252
"""
232253
rad2deg(x)
233254
@@ -241,7 +262,7 @@ julia> rad2deg(pi)
241262
180.0
242263
```
243264
"""
244-
rad2deg(z::AbstractFloat) = z * (180 / oftype(z, pi))
265+
rad2deg(z::AbstractFloat) = z * _180_over_pi(z)
245266

246267
"""
247268
deg2rad(x)
@@ -256,7 +277,7 @@ julia> deg2rad(90)
256277
1.5707963267948966
257278
```
258279
"""
259-
deg2rad(z::AbstractFloat) = z * (oftype(z, pi) / 180)
280+
deg2rad(z::AbstractFloat) = z * _pi_over_180(z)
260281
rad2deg(z::Real) = rad2deg(float(z))
261282
deg2rad(z::Real) = deg2rad(float(z))
262283
rad2deg(z::Number) = (z/pi)*180

test/math.jl

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,14 +448,24 @@ end
448448
end
449449

450450
@testset "deg2rad/rad2deg" begin
451-
@testset "$T" for T in (Int, Float64, BigFloat)
451+
@testset "$T" for T in (Int, Float16, Float32, Float64, BigFloat)
452452
@test deg2rad(T(180)) 1pi
453453
@test deg2rad.(T[45, 60]) [pi/T(4), pi/T(3)]
454454
@test rad2deg.([pi/T(4), pi/T(3)]) [45, 60]
455455
@test rad2deg(T(1)*pi) 180
456456
@test rad2deg(T(1)) rad2deg(true)
457457
@test deg2rad(T(1)) deg2rad(true)
458458
end
459+
@testset "accuracy" begin
460+
@testset "$T" for T in (Float16, Float32, Float64)
461+
@test rad2deg(T(1)) === setprecision(BigFloat, 500) do
462+
T(180 / BigFloat(pi))
463+
end
464+
@test deg2rad(T(1)) === setprecision(BigFloat, 500) do
465+
T(BigFloat(pi) / 180)
466+
end
467+
end
468+
end
459469
@test deg2rad(180 + 60im) pi + (pi/3)*im
460470
@test rad2deg(pi + (pi/3)*im) 180 + 60im
461471
end

0 commit comments

Comments
 (0)