Finding the angle between two bearings is often confusing.[1]



Task

Find the angle which is the result of the subtraction b2 - b1, where b1 and b2 are the bearings.


Input bearings are expressed in the range   -180   to   +180   degrees.

The  result  is also expressed in the range   -180   to   +180   degrees.



Compute the angle for the following pairs:

 20 degrees (b1) and 45 degrees (b2)
 -45 and 45
 -85 and 90
 -95 and 90
 -45 and 125
 -45 and 145
 29.4803 and -88.6381
 -78.3251 and -159.036


Optional extra
 
Allow the input bearings to be any (finite) value.



Test cases

 -70099.74233810938 and 29840.67437876723
 -165313.6666297357 and 33693.9894517456
 1174.8380510598456 and -154146.66490124757
 60175.77306795546 and 42213.07192354373


import Control.Monad (join)
import Data.Bifunctor (bimap)
import Text.Printf (printf)
 
type Radians = Float
 
type Degrees = Float
 
---------- ANGLE DIFFERENCE BETWEEN TWO BEARINGS ---------
 
bearingDelta :: (Radians, Radians) -> Radians
bearingDelta (a, b) -- sign * dot-product
  =
  sign * acos ((ax * bx) + (ay * by))
  where
    (ax, ay) = (sin a, cos a)
    (bx, by) = (sin b, cos b)
    sign
      | ((ay * bx) - (by * ax)) > 0 = 1
      | otherwise = -1
 
angleBetweenDegrees :: (Degrees, Degrees) -> Degrees
angleBetweenDegrees =
  degrees
    . bearingDelta
    . join bimap radians
 
--------------------------- TEST -------------------------
main :: IO ()
main =
  putStrLn . unlines $
    fmap
      ( uncurry (printf "%6.2f° - %6.2f°  ->  %7.2f°")
          <*> angleBetweenDegrees
      )
      [ (20.0, 45.0),
        (-45.0, 45.0),
        (-85.0, 90.0),
        (-95.0, 90.0),
        (-45.0, 125.0),
        (-45.0, 145.0)
      ]
 
------------------------- GENERIC ------------------------
 
degrees :: Radians -> Degrees
degrees = (/ pi) . (180 *)
 
radians :: Degrees -> Radians
radians = (/ 180) . (pi *)