in reply to Re^6: How to write testable command line script?
in thread How to write testable command line script?

update: AnomalousMonk said it way better than I did. Original left for historical purposes.


You've misunderstood. I am claiming you are representing negative angles incorrectly. After the subtraction, if you get -60 seconds, you should have the equivalent of -1min. -1min is represented canonically as -0° 1' 0", not as -1° 59' 0". The answer you list in Re^2: How to write testable command line script? of my $test_7 = [ 0, 0, -60]; my $answer_7 = [-1, 59,  0]; is wrong. Honestly, your format doesn't allow you to use the right answer, unless you know how to get perl to store the "-0" correctly (for example, using text), because it should be my $answer_7 = [-0, 1, 0];.

regarding normal base-10 subtraction, when you subtract 0.1 from 0.0, you do NOT borrow one from the ones column, making it -1; then have 10 in the tenths column, and subtract 1, leaving 9/10ths, or -1.9. 0.0-0.1 is obviously -0.1. What you are claiming in DMS is exactly equivalent to claiming that -60sec is -1° 59' 0".

 0.0        Try borrowing: (0-1=-1).(0+10=10) => (-1).(10)
-0.1                                            -( 0).( 1)
====                                            ==========
-0.1                                             (-1).( 9)  => -1.9

That's nonsensical, so obviously not the right method for subtracting a bigger from a smaller.

To subtract a bigger number (M) from a smaller number (S) to do a difference D=M-S, you have to do -(S-M)=D
So start with the S-M:
 0.1
-0.0
====
 0.1
Then negate the answer
-0.1


The same is true in other bases, like the base 60 of DMS:
 0°00'00"
-0°00'60"
========
??°??'??"

You start by making the bigger the first,
 0°00'60"
-0°00'00"
========
????????
Then simplify the top term and subtract
 0°01'00"
-0°00'00"
========
 0°01'00"

The negate: 
-0°1'0"

However you calculate it, whether you do it in decimal degrees, or in DMS, the only difference between -60s and +60s should be the sign at the beginning. +60s is (0° 1' 0"), and -60s is -(0° 1' 0"), which gets simplified to -0° 1' 0", exactly like -(0.016) gets simplified to -0.016 -- the negative sign is placed next to the most significant digit, but implies the whole result is negative. -0.016deg converts to -(0.016deg + 0/60 + 0/3600) = -(0 + 1/60 + 0/3600) = -(0° 1' 0"). You do not interpret -0.016 = (-0) + (0/10) + (1/100) + (6/1000) -- because that math evaluates to +16/1000, which is wrong. You evaluate -0.016 as -(0.016) = -(0 + 0/10 + 1/100 + 6/1000) = -(16/1000), which is right; or, in the other direction (-16 thousandths) = -(16 thousandths) = -(1 hundredth + 6 thousandths) = -(zero ones + 0 tenths + 1 hundredths + 6 thousandths). In the same way, -60sec = -(60sec) = -(0 + 0/60 + 60/3600) = -(0 + 1/60 + 0/3600) = -(0° 1' 0") = -0° 1' 0". Negative Degress minutes seconds (-x°y'z") is not (-x°) + (y') + (z") = -x + y/60 + z/3600, it is -(x° + y' + z") -(x + y/60 + z/3600).

Have I said this in a way that makes my point more clear?

the resource https://books.google.com/books?id=YRNeBAAAQBAJ&lpg=PR1&dq=Albert%20Klaf's%20Trigonometry%20Refresher&pg=PA12#v=onepage&q&f=false did not show an example when the result is negative. I do not believe you are extending into negative results correctly.

(and linking works just fine to me: click on the LINK icon once you've navigated to the google books page, copy the URL, and paste it here between square brackets, like [https://books.google.com/books?id=YRNeBAAAQBAJ&lpg=PR1&dq=Albert%20Klaf's%20Trigonometry%20Refresher&pg=PA12#v=onepage&q&f=false].

Replies are listed 'Best First'.
Re^8: How to write testable command line script?
by thechartist (Monk) on Nov 27, 2018 at 04:43 UTC

    I appreciate your input, and I generally agree my representation is not optimal for extension, but I want to clarify what my algorithm does, because this is not a correct understanding of the representation.

    The algorithm is based on complement arithmetic, which is why I didn't give it too much thought. The Japanese soroban is based upon it; ie. When subtracting 2 - 8, you add the complement of 8, in base 10 is 2, because 2+8 = 10. The problem of 2-8 becomes (2+2)-10 = -6. In base 60, we would add the complement of 60 and subtract a 1 from the column on the left, until we get to the degrees, which are in decimal format.

    For the very reasons you outlined (Perl's representation of small negatives), I represent -0.1 as an equivalent polynomial (-1 + 9/10), or in this case 0 -1 0 as (-1 + 59/60 + 0), with the denominator implied as (-1 59 0).

    Algebraically, a + (-a) = 0. If (0 -1 0) + (0 1 0) = (0 0 0), then we must treat -1 59 0 as equivalent to 0 -1 0, because (-1 59 0) + (0 1 0) = (-1 60 0) = (0 0 0).

      Okay, I understand better where you're coming from. For an internal representation, that's fine.

      However, don't expect users to work in that same system for input and output: in my experience, people thinking in terms of DMS will usually be thinking in terms of London's longitude as -0°7′39″ = -(0°7′39″), not as = (-1°) + 52′ + 21″. If your program outputted -1 52 21, someone looking at a map of England would be forgiven for assuming you were printing the longitude of (... /me searches a map for an example location...) slightly west of Swindon, not the longitude of London -- and if I entered -1 52 21, I would expect your program to interpret that as being near Swindon, not London.

      For internals, I'm now in agreement with AnomalousMonk's post, where it was recommended to do everything internally as arc-seconds. I now like a that a lot more than doing everything in decimal degrees: the fractions 1/60 and 1/3600 don't get represented well in internal binary floating-point notation, whereas -459 arc-seconds is stored quite simply as an integer; this also makes the internal angle representation more like the time representation (seconds since the epoch); I also think that would make all the internal math much simpler.

Re^8: How to write testable command line script?
by thechartist (Monk) on Nov 26, 2018 at 17:51 UTC

    Ah. I think I understand now. Thanks for catching my error. I have no idea why I didn't consider that initially. Back to the drawing board.

    Edit: What do you think the best way of representing the degree:min:sec is? I am leaning to simply converting everything to seconds, doing the arithmetic, and then converting to deg:min:sec format as Anomalous Monk did. I planned on extending the calculator to accept decimals anyway. But if I wanted to accept integer input, what would be the best way of representing this situation of small negatives?

    Storing the -0 as text does not appeal to me.

      If you want to avoid "-0", my order of prefence would be: 1) store it as decimal; 2) store it as [$sign, $deg, $min, $sec] where all four are integers, with sign taking on -1 for negative angles; 3) store it as [$deg, $min, $sec, $sign] (but I don't like that order as much).

      For accepting integer input, I'd just only allow sign on the degrees... so if you wanted to allow "-0 1 0", that would be interpreted as -1min. Since "input" implies "text" in my mind, you can just check for (and strip) the sign using text processing, and then re-apply the sign once you are done:

      $input = "-0 1 0"; my $sign = ($input =~ /^\s*-/) ? -1 : 1; my ($d,$m,$s) = map abs, split / /, $input; my $decdegrees = $sign * ($d + $m/60 + $s/3600); print $decdegrees
      ... or maybe ...
      $input = "-0 1 0"; my ($sign, $d, $m, $s) = ($input =~ /(-{0,1})(\d+)\s+(\d+)\s+(\d+)/); my $decdegrees = (($sign)?-1:1) * ($d + $m/60 + $s/3600); print $decdegrees
      .

      update: fix missing code tags