Angle difference between two bearings

From Rosetta Code
Angle difference between two bearings is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

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

Task

We need to find the angle which is the result of the subtraction b2-b1, where b1 and b2 are both bearings. Input bearings are expressed by numbers in the range -180 to +180 degrees. The result must also be 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

C++

<lang cpp>#include <cmath>

  1. include <iostream>

using namespace std;

double getDifference(double b1, double b2) { double r = b2-b1; int a = ((int)r) / 360.0; r -= a * 360.0; if (r < -180.0) r += 360.0; if (r >= 180.0) r -= 360.0; return r; }

int main() { cout << "Input in -180 to +180 range" << endl; cout << getDifference(20.0, 45.0) << endl; cout << getDifference(-45.0, 45.0) << endl; cout << getDifference(-85.0, 90.0) << endl; cout << getDifference(-95.0, 90.0) << endl; cout << getDifference(-45.0, 125.0) << endl; cout << getDifference(-45.0, 145.0) << endl; cout << getDifference(-45.0, 125.0) << endl; cout << getDifference(-45.0, 145.0) << endl; cout << getDifference(29.4803, -88.6381) << endl; cout << getDifference(-78.3251, -159.036) << endl;

cout << "Input in wider range" << endl; cout << getDifference(-70099.74233810938, 29840.67437876723) << endl; cout << getDifference(-165313.6666297357, 33693.9894517456) << endl; cout << getDifference(1174.8380510598456, -154146.66490124757) << endl; cout << getDifference(60175.77306795546, 42213.07192354373) << endl;

return 0; }</lang>

Output:
Input in -180 to +180 range
25
90
175
-175
170
-170
170
-170
-118.118
-80.7109
Input in wider range
-139.583
-72.3439
-161.503
37.2989

Haskell

<lang Haskell>import Text.Printf

type Radians = Float

type Degrees = Float

angleBetweenDegrees :: Degrees -> Degrees -> Degrees angleBetweenDegrees a b = degrees $ relBearing (radians a) (radians b)

relBearing :: Radians -> Radians -> Radians relBearing 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 -- cross-product > 0 ?
    =
     if ((ay * bx) - (by * ax)) > 0
       then 1
       else (-1)

degrees :: Radians -> Degrees degrees = (/ pi) . (180 *)

radians :: Degrees -> Radians radians = (/ 180) . (pi *)

main :: IO () main =

 mapM_ putStrLn $
 displayRow <$>
 [ (20.0, 45.0)
 , (-45.0, 45.0)
 , (-85.0, 90.0)
 , (-95.0, 90.0)
 , (-45.0, 125.0)
 , (-45.0, 145.0)
 ]
 where
   displayRow (x, y) =
     printf "%6.2f° + %6.2f°  ->  %7.2f°" x y $ angleBetweenDegrees x y</lang>
Output:
 20.00° +  45.00°  ->    25.00°
-45.00° +  45.00°  ->    90.00°
-85.00° +  90.00°  ->   175.00°
-95.00° +  90.00°  ->  -175.00°
-45.00° + 125.00°  ->   170.00°
-45.00° + 145.00°  ->  -170.00°

J

<lang j>relativeBearing=: -&360^:(180&<)@(360 | 720 + -~)/</lang> <lang j>tests=: _99&".;._2 noun define 20 45 -45 45 -85 90 -95 90 -45 125 -45 145 29.4803 -88.6381 -78.3251 -159.036 -70099.74233810938 29840.67437876723 -165313.6666297357 33693.9894517456 1174.8380510598456 -154146.66490124757 60175.77306795546 42213.07192354373 )

  tests ,. relativeBearing"1 tests
     20       45       25
    _45       45       90
    _85       90      175
    _95       90     _175
    _45      125      170
    _45      145     _170
29.4803 _88.6381 _118.118

_78.3251 _159.036 _80.7109 _70099.7 29840.7 _139.583

_165314    33694 _72.3439
1174.84  _154147 _161.503
60175.8  42213.1  37.2989</lang>

JavaScript

ES5

This approach should be reliable but it is also very inefficient.

<lang javascript>function relativeBearing(b1Rad, b2Rad) { b1y = Math.cos(b1Rad); b1x = Math.sin(b1Rad); b2y = Math.cos(b2Rad); b2x = Math.sin(b2Rad); crossp = b1y * b2x - b2y * b1x; dotp = b1x * b2x + b1y * b2y; if(crossp > 0.) return Math.acos(dotp); return -Math.acos(dotp); }

function test() { var deg2rad = 3.14159265/180.0; var rad2deg = 180.0/3.14159265; return "Input in -180 to +180 range\n" +relativeBearing(20.0*deg2rad, 45.0*deg2rad)*rad2deg+"\n" +relativeBearing(-45.0*deg2rad, 45.0*deg2rad)*rad2deg+"\n" +relativeBearing(-85.0*deg2rad, 90.0*deg2rad)*rad2deg+"\n" +relativeBearing(-95.0*deg2rad, 90.0*deg2rad)*rad2deg+"\n" +relativeBearing(-45.0*deg2rad, 125.0*deg2rad)*rad2deg+"\n" +relativeBearing(-45.0*deg2rad, 145.0*deg2rad)*rad2deg+"\n"

+relativeBearing(29.4803*deg2rad, -88.6381*deg2rad)*rad2deg+"\n" +relativeBearing(-78.3251*deg2rad, -159.036*deg2rad)*rad2deg+"\n"

+ "Input in wider range\n" +relativeBearing(-70099.74233810938*deg2rad, 29840.67437876723*deg2rad)*rad2deg+"\n" +relativeBearing(-165313.6666297357*deg2rad, 33693.9894517456*deg2rad)*rad2deg+"\n" +relativeBearing(1174.8380510598456*deg2rad, -154146.66490124757*deg2rad)*rad2deg+"\n" +relativeBearing(60175.77306795546*deg2rad, 42213.07192354373*deg2rad)*rad2deg+"\n";

}</lang>

Output:
Input in -180 to +180 range
25.000000000000004
90
174.99999999999997
-175.00000041135993
170.00000000000003
-170.00000041135996
-118.1184
-80.71089999999998
Input in wider range
-139.5833974814558
-72.34414600076728
-161.50277501127033
37.2988761562732

ES6

<lang JavaScript>(() => {

   const
       [Pi, sin, cos, acos] =
       ['PI', 'sin', 'cos', 'acos']
       .map(k => Math[k]),
       degRad = x => Pi * x / 180.0,
       radDeg = x => 180.0 * x / Pi;


   // relBearing :: Radians -> Radians -> Radians
   const relBearing = (ar, br) => {
       const
           [ax, ay] = [sin(ar), cos(ar)],
           [bx, by] = [sin(br), cos(br)],
           // Cross-product > 0 ?
           sign = ((ay * bx) - (by * ax)) > 0 ? +1 : -1;
       // Sign * dot-product
       return sign * acos((ax * bx) + (ay * by));
   }
   // TEST
   // justifyRight :: Int -> Char -> Text -> Text
   const justifyRight = (n, cFiller, strText) =>
       n > strText.length ? (
           (cFiller.repeat(n) + strText)
           .slice(-n)
       ) : strText;


   // showMap :: Degrees -> Degrees -> String
   const showMap = (da, db) =>
       justifyRight(6, ' ', `${da}° +`) +
       justifyRight(11, ' ',` ${db}°  ->  `) +
       justifyRight(7, ' ', `${(radDeg(relBearing(degRad(da), degRad(db))))
           .toPrecision(4)}°`);
   return [
           [20, 45],
           [-45, 45],
           [-85, 90],
           [-95, 90],
           [-45, 125],
           [-45, 145]
       ].map(xy => showMap(...xy))
       .join('\n');

})();</lang>

Output:
 20° +  45°  ->   25.00°
-45° +  45°  ->   90.00°
-85° +  90°  ->   175.0°
-95° +  90°  ->  -175.0°
-45° + 125°  ->   170.0°
-45° + 145°  ->  -170.0°

Perl 6

Works with: Rakudo version 2016.11

<lang perl6>sub infix:<∠> (Rat $b1, Rat $b2) {

  (my $b = ($b2 - $b1 + 720) % 360) > 180 ?? $b - 360 !! $b;

}

  1. TESTING

for 20.0, 45.0,

  -45.0, 45.0,
  -85.0, 90.0,
  -95.0, 90.0,
  -45.0, 125.0,
  -45.0, 145.0,
  29.4803, -88.6381,
  -78.3251, -159.036,
  -70099.74233810938, 29840.67437876723,
  -165313.6666297357, 33693.9894517456,
  1174.8380510598456, -154146.66490124757,
  60175.77306795546, 42213.07192354373


 -> $b1, $b2 { say "$b1 ∠ $b2 = ", $b1 ∠ $b2 }</lang>
Output:
20 ∠ 45 = 25
-45 ∠ 45 = 90
-85 ∠ 90 = 175
-95 ∠ 90 = -175
-45 ∠ 125 = 170
-45 ∠ 145 = -170
29.4803 ∠ -88.6381 = -118.1184
-78.3251 ∠ -159.036 = -80.7109
-70099.74233810938 ∠ 29840.67437876723 = -139.58328312339
-165313.6666297357 ∠ 33693.9894517456 = -72.3439185187
1174.8380510598456 ∠ -154146.66490124757 = -161.5029523074156
60175.77306795546 ∠ 42213.07192354373 = 37.29885558827

Python

Translation of: C++

<lang python>from __future__ import print_function

def getDifference(b1, b2): r = (b2 - b1) % 360.0 # Python modulus has same sign as divisor, which is positive here, # so no need to consider negative case if r >= 180.0: r -= 360.0 return r

if __name__ == "__main__": print ("Input in -180 to +180 range") print (getDifference(20.0, 45.0)) print (getDifference(-45.0, 45.0)) print (getDifference(-85.0, 90.0)) print (getDifference(-95.0, 90.0)) print (getDifference(-45.0, 125.0)) print (getDifference(-45.0, 145.0)) print (getDifference(-45.0, 125.0)) print (getDifference(-45.0, 145.0)) print (getDifference(29.4803, -88.6381)) print (getDifference(-78.3251, -159.036))

print ("Input in wider range") print (getDifference(-70099.74233810938, 29840.67437876723)) print (getDifference(-165313.6666297357, 33693.9894517456)) print (getDifference(1174.8380510598456, -154146.66490124757)) print (getDifference(60175.77306795546, 42213.07192354373))</lang>

Output:
Input in -180 to +180 range
25.0
90.0
175.0
-175.0
170.0
-170.0
170.0
-170.0
-118.11840000000001
-80.71089999999998
Input in wider range
-139.58328312338563
-72.34391851868713
-161.50295230740448
37.29885558826936

Racket

see my comments in discussion regards bearing-heading or vice versa

<lang racket>#lang racket (define (% a b) (- a (* b (truncate (/ a b)))))

(define (bearing- bearing heading)

 (- (% (+ (% (- bearing heading) 360) 540) 360) 180))

(module+ main

 (bearing- 20 45)
 (bearing- -45 45)
 (bearing- -85 90)
 (bearing- -95 90)
 (bearing- -45 125)
 (bearing- -45 145)
 (bearing- 29.4803 -88.6381)
 (bearing- -78.3251 -159.036)
 (bearing- -70099.74233810938 29840.67437876723)
 (bearing- -165313.6666297357 33693.9894517456)
 (bearing- 1174.8380510598456 -154146.66490124757)
 (bearing- 60175.77306795546 42213.07192354373))

(module+ test

 (require rackunit)
 (check-equal? (% 7.5 10) 7.5)
 (check-equal? (% 17.5 10) 7.5)
 (check-equal? (% -7.5 10) -7.5)
 (check-equal? (% -17.5 10) -7.5))</lang>
Output:
-25
-90
-175
175
-170
170
118.11839999999995
80.71090000000004
139.58328312338563
72.34391851868713
161.50295230740448
-37.29885558826936

REXX

A little extra coding was added for a better visual presentation;   the angles were centered, the answers were aligned. <lang rexx>/*REXX pgm calculates the difference between 2 angles (degrees), normalizes the result. */ numeric digits 25 /*use enough decimal diigits for angles*/ call show 20, 45 /*display the angular difference (deg).*/ call show -45, 45 /* " " " " " */ call show -85, 90 /* " " " " " */ call show -95, 90 /* " " " " " */ call show -45, 125 /* " " " " " */ call show 45, 145 /* " " " " " */ call show 29.4803, -88.6361 /* " " " " " */ call show -78.3251, -159.036 /* " " " " " */ call show -70099.74233810938, 29840.67437876723 /* " " " " " */ call show -165313.6666297357, 33693.9894517456 /* " " " " " */ call show 1174.8380510598456,-154146.66490124757 /* " " " " " */ call show 60175.773067955546, 42213.07192354373 /* " " " " " */ exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ show: procedure; parse arg a,b; d=digits(); $='º' /*obtain the 2 angles (are in degrees)*/

     x=format( ( ( ((b-a) // 360) + 540) // 360) - 180, 4, d-4) /*compute and format.  */
     if pos(., x)\==0  then x=strip( strip(x, 'T', 0), "T", .)  /*strip trailing chaff.*/
     say center(a || $, d)      '─'      center(b || $, d)         "───►"         x || $
     return                                      /* [↑]  display the angular difference*/</lang>

output

           20º            ─            45º            ───►   25º
          -45º            ─            45º            ───►   90º
          -85º            ─            90º            ───►  175º
          -95º            ─            90º            ───► -175º
          -45º            ─           125º            ───►  170º
           45º            ─           145º            ───►  100º
        29.4803º          ─         -88.6361º         ───► -118.1164º
        -78.3251º         ─         -159.036º         ───►  -80.7109º
   -70099.74233810938º    ─    29840.67437876723º     ───► -139.58328312339º
   -165313.6666297357º    ─     33693.9894517456º     ───►  -72.3439185187º
   1174.8380510598456º    ─   -154146.66490124757º    ───► -161.5029523074156º
   60175.773067955546º    ─    42213.07192354373º     ───►   37.298855588184º

zkl

Translation of: Perl 6

<lang zkl>fcn bearingAngleDiff(b1,b2){ // -->Float, b1,b2 can be int or float

 ( (b:=(0.0 + b2 - b1 + 720)%360) > 180 ) and b - 360 or b;

}</lang> <lang zkl>T( 20,45, -45,45, -85,90, -95,90, -45,125, -45,145 ) .pump(Console.println,Void.Read,

     fcn(b1,b2){ "%.1f\UB0; + %.1f\UB0; = %.1f\UB0;"
                 .fmt(b1,b2,bearingAngleDiff(b1,b2)) });</lang>
Output:
20.0° + 45.0° = 25.0°
-45.0° + 45.0° = 90.0°
-85.0° + 90.0° = 175.0°
-95.0° + 90.0° = -175.0°
-45.0° + 125.0° = 170.0°
-45.0° + 145.0° = -170.0°

References