ITADN

use atan2 in V2.unangle

#177Openaavogt 创建于 2024-04-04
A
aavogtcommented
I propose `unangle (V2 x y) = atan2 y x` because it's shorter and numerically better: ``` ghci> unangle (V2 0 2.646978e-23) :: Float NaN ghci> unangle (V2 0 2.6469783e-23) :: Float 0.7853983 ghci> unangle (V2 0 5.4210115e-20) :: Float 1.5703081 ghci> atan2 1e-45 0 :: Float 1.5707964 ``` But the output of unangle is on [-pi/2, 3pi/2], while the output of atan2 is on [-pi,pi]: ```haskell > do printf "unangle, atan2, angle\n" ; sequence_ [ printf "%.2f %.2f %.2f\n" (unangle v) (atan2 y x) ang | ang <- [ -2*pi, -2*pi + pi/4 .. 2*pi ], let v = angle ang, let V2 x y = v ] :: IO () unangle, atan2, angle 0.00 0.00 -6.28 0.79 0.79 -5.50 1.57 1.57 -4.71 2.36 2.36 -3.93 3.14 -3.14 -3.14 3.93 -2.36 -2.36 -1.57 -1.57 -1.57 -0.79 -0.79 -0.79 0.00 0.00 0.00 0.79 0.79 0.79 1.57 1.57 1.57 2.36 2.36 2.36 3.14 3.14 3.14 3.93 -2.36 3.93 4.71 -1.57 4.71 -0.79 -0.79 5.50 -0.00 -0.00 6.28 ``` You could keep the old range but I suspect it's not necessary: ```haskell -- | output on [-pi/2, 3pi/2] unangle (V2 x y) = case atan2 y x of theta | theta < -pi/2 -> theta + 2*pi | otherwise -> theta ``` A bigger problem with the new definition is that the type will change. Some users of unangle need new RealFloat instances: ```haskell unangle :: (Floating a, Ord a) => V2 a -> a atan2 :: RealFloat a => a -> a -> a instance Floating a => Floating (V2 a) -- exists instance RealFloat a => RealFloat (V2 a) where atan2 = liftA2 atan2 -- missing for good reason.. how to write encode/decodeFloat? ``` OverlappingInstances, TypeFamilies or maybe rewrite rules are another option to get atan2 for Float and Double, but keep asin for other types.
0 条评论