HP Forums

Full Version: newRPL: Angles revisited
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2
A few months back there was a discussion on how to treat angular values in trig functions.

Using units of angle wasn't practical because angles are in reality unitless, and adding units makes them inconsistent with a simple numeric input.

So here's another proposal: Make angles first-class citizens, with its own object type, and its own syntax.
With this system, an angle is basically a "tagged" real number, with the tag indicating: first that it's an angle, and second, which angle system it's using. The syntax could be:
∡90 (in the current system angle mode)
∡90° (in degrees)
∡90.1030d (in DMS notation)
∡1.57r (in radians)
∡200g (in grads)

Operations involving angles and "normal" numbers will disregard the unit (except for DMS notation, which needs to be converted first to degrees).
Operations between angles and between angles and numbers will keep the tag of the first argument (consistent with the behavior used for binary integers).

∡90° 1 + --> ∡91°

1 ∡90° + --> 91

∡90.3000d 1.5 + --> ∡92.0000d

1.5 ∡90.3000d + --> 92

These angle quantities can be freely used as numbers inside other objects, for example:
(1 , ∡90°) becomes a complex expressed in polar notation.
[1 ∡90°] same thing for 2D vectors
[1 ∡45° ∡90°] Spherical polar vector
[1 ∡90° 1] Cylindrical polar vector
[∡90° ∡45° ∡90°] Spherical polar vector with magnitude 90 (first angle will be interpreted as just a number)

EDIT: Forgot to mention, you can mix and match different angle units in a spherical vector.

Functions like SIN would recognize the angle in arguments properly.
Commands like ASIN will always return angle objects in the current angle system. Since these objects also behave like numbers, this wouldn't break compatibility with old code.

A command to convert between angle types would have to be provided, although adding 0 would be just as effective:

∡0° ∡90.3000d + --> ∡90.5°

Or even better, commands to force an angle into a particular mode:
→∡r would take any number and tag it as an angle in radians. Also convert to radians if the argument is already an angle.
→∡d same for DMS.
→∡°
→∡g

-1 ASIN would return ∡-90° if the current mode is DEG.
-1 ASIN →∡r would guarantee a result in radians regardless of current mode.
→∡d SIN would force SIN to interpret its input as DMS, regardless of the current flags. If the user gives a number, the given number will be "tagged" as an angle in DMS.

For a CAS system, SIN should be forced into radians regardless of system flags, so instead of asking the user "switch to radians mode?", perhaps SIN should ALWAYS interpret numbers as radians, and the user needs to use the proper →∡° command if the input is not in radians. This would break compatibility with old code using degrees, but would make SIN consistent with the CAS behavior.

Unit objects would also have angle units, I gues they would have to coexist (??)

Any thoughts?
Something to consider:

When writing, we write from left to right (most of us). So when you write ∡90° 1 + --> ∡91° it makes sense as the angle appears "first", and one can easily deduce that the type of "first" argument takes precedence. However, on a stack, the data-value entered last is actually "first." While I don't really care either way, I just thought it might be something to consider when finalizing the type priority.

Also, is it safe to assume that the tag/unit gets dropped when used in operations that would normally either not make sense or not take angles as arguments? For example, what would ∡12° 2 ^ produce as a result? Should it be ∡144°, or perhaps some sort of "squared" angle if we are treating the value as a unit, or simply the number 144?
(03-31-2016 06:13 PM)Claudio L. Wrote: [ -> ]Operations between angles and between angles and numbers will keep the tag of the first argument (consistent with the behavior used for binary integers).

∡90° 1 + --> ∡91°

1 ∡90° + --> 91

Interesting and likely useful extension, as long as it can't break existing code. The examples shown imply this, but there are a lot of possibilities...

One question though about your comment "...consistent with the behavior used for binary integers."

If I understand your intention here (and maybe I don't) this does not seem correct.

5 #1 + --> #6, not 6 as the example implies.

Again, maybe I'm not following...
A long time ago I wrote an RPN calculator program for the Casio FX-9860G calculator that uses angle tags in a similar way. I liked it; it would be nice to see something similar in newRPL.

My program has two buttons (D and R) which tag the number in the x register as degrees or radians respectively. If the x register already contains an angle tagged with D then pressing R converts it to radians and changes the tag to R, while pressing D removes the tag. So tagging and conversion are done with the same function.

In my program:
  • Adding or subtracting two angles (e.g., 2D 1R +) gives an answer with the angle tag of the current angle mode. Adding or subtracting an angle and an untagged number works in the same way with the untagged number being implicitly tagged with the current angle mode. I don't like the idea of adding a number to an angle giving a number. In radian mode, 1 1D + and 1D 1 + should both mean the sum of 1 radian and one degree and should be tagged as an angle. I'm not too bothered about whether it is tagged as radians or degrees, but it is definitely an angle!
  • There is no implicit tag when multiplying or dividing an angle by an untagged number.
  • Dividing one angle by another gives an untagged result - the ratio of the two angles.
  • In every other case (e.g., 2D 1R *) the angles are converted to the current angle mode, the tags are removed, and the calculation is carried out. It might be more sensible to throw an error in situations like these, although powers of angles might be meaningful in some cases.
  • Dealing with trig and hyperbolic trig functions with complex arguments needs care!

Nigel (UK)
(03-31-2016 06:49 PM)Han Wrote: [ -> ]Something to consider:

When writing, we write from left to right (most of us). So when you write ∡90° 1 + --> ∡91° it makes sense as the angle appears "first", and one can easily deduce that the type of "first" argument takes precedence. However, on a stack, the data-value entered last is actually "first." While I don't really care either way, I just thought it might be something to consider when finalizing the type priority.

You're right, let's clarify:
I always call first argument to the left argument in a binary operator, and second argument the right argument.
I never refer to the arguments by stack level number.

(03-31-2016 06:49 PM)Han Wrote: [ -> ]Also, is it safe to assume that the tag/unit gets dropped when used in operations that would normally either not make sense or not take angles as arguments? For example, what would ∡12° 2 ^ produce as a result? Should it be ∡144°, or perhaps some sort of "squared" angle if we are treating the value as a unit, or simply the number 144?

Good point, I think besides addition and subtraction, all other operations should produce real numbers as results, as they don't make any sense with angles.
For example, ∡12° 360 / is no longer an angle, but a fraction of a turn, so it makes sense to forget about the angle tag. If you really need to keep track of the angle through an expression like this, you should use units instead.
However, adding and subtracting should preserve the format of the first (left) argument.
(03-31-2016 06:54 PM)rprosperi Wrote: [ -> ]Interesting and likely useful extension, as long as it can't break existing code. The examples shown imply this, but there are a lot of possibilities...

One question though about your comment "...consistent with the behavior used for binary integers."

If I understand your intention here (and maybe I don't) this does not seem correct.

5 #1 + --> #6, not 6 as the example implies.

Again, maybe I'm not following...

Have you actually installed and tried newRPL yet? (you got caught!)
It's about time... I think you'd be pleasantly surprised.

5 #1h + gives 6
#1h 5 + gives #6h
#1 or #1d are aliases for 1, the decimal base doesn't need the # at all in newRPL.
(03-31-2016 08:40 PM)Nigel (UK) Wrote: [ -> ]A long time ago I wrote an RPN calculator program for the Casio FX-9860G calculator that uses angle tags in a similar way. I liked it; it would be nice to see something similar in newRPL.

My program has two buttons (D and R) which tag the number in the x register as degrees or radians respectively. If the x register already contains an angle tagged with D then pressing R converts it to radians and changes the tag to R, while pressing D removes the tag. So tagging and conversion are done with the same function.

In my program:
  • Adding or subtracting two angles (e.g., 2D 1R +) gives an answer with the angle tag of the current angle mode. Adding or subtracting an angle and an untagged number works in the same way with the untagged number being implicitly tagged with the current angle mode. I don't like the idea of adding a number to an angle giving a number. In radian mode, 1 1D + and 1D 1 + should both mean the sum of 1 radian and one degree and should be tagged as an angle. I'm not too bothered about whether it is tagged as radians or degrees, but it is definitely an angle!
  • There is no implicit tag when multiplying or dividing an angle by an untagged number.
  • Dividing one angle by another gives an untagged result - the ratio of the two angles.
  • In every other case (e.g., 2D 1R *) the angles are converted to the current angle mode, the tags are removed, and the calculation is carried out. It might be more sensible to throw an error in situations like these, although powers of angles might be meaningful in some cases.
  • Dealing with trig and hyperbolic trig functions with complex arguments needs care!

Nigel (UK)

It seems we think alike. Your tags are pretty much the same thing I'm proposing.
The only difference is my proposal of keeping the format of the first argument.
My logic is a bit different, I think of operations as something applied to the number that's on the stack. For example, you have a hex number and you want to increase it. For easy typing, you just want to type 1 +, but you still want the result to be a hex number, hence keeping the first argument format is useful. Same concept helps with temperatures, etc.

For angles, I think the same applies: if you have an angle, it's reasonable to assume if you add a number to it you are adding another angle and want the result to be an angle, you just don't type it as an angle to save keystrokes.
However, when you have a real number and you add an angle, I'm not sure you are working with numbers or angles, so the type should remain "unknown"= real number. At least that's my logic, or it was until I read your post.
I think your logic makes sense too, but I have to think a little more before I break the consistency with all the other parts of newRPL.
(04-01-2016 12:55 AM)Claudio L. Wrote: [ -> ]Have you actually installed and tried newRPL yet? (you got caught!)
It's about time... I think you'd be pleasantly surprised.

5 #1h + gives 6
#1h 5 + gives #6h
#1 or #1d are aliases for 1, the decimal base doesn't need the # at all in newRPL.

Caught red-handed! I thought you were referring to "oldRPL" aka the 48/49/50 flavor...

I guess I missed when this was changed in newRPL. I can go back and read why, implications, etc.

I have not flashed with newRPL yet for the obvious reason: so many toys, so little time... I'll catch up eventually. And newRPL will be even better then.
(04-01-2016 02:10 AM)rprosperi Wrote: [ -> ]Caught red-handed! I thought you were referring to "oldRPL" aka the 48/49/50 flavor...

I guess I missed when this was changed in newRPL. I can go back and read why, implications, etc.

I have not flashed with newRPL yet for the obvious reason: so many toys, so little time... I'll catch up eventually. And newRPL will be even better then.

And now I have your written confession... guilty as charged! Bring the guillotine!
On second thought, I'll just write your name in the "users I still need to convince by improving newRPL to perfection" list, and I'll forgive you.

Claudio
(04-01-2016 12:50 AM)Claudio L. Wrote: [ -> ]For example, ∡12° 360 / is no longer an angle

Why?
For me, 10°+10°+10° = 3x10° = 30°, and 30°/3 = 10°.
I think that an angle multiplied or divided by a constant (a real number) should be an angle.
(04-01-2016 05:33 PM)Helix Wrote: [ -> ]
(04-01-2016 12:50 AM)Claudio L. Wrote: [ -> ]For example, ∡12° 360 / is no longer an angle

Why?
For me, 10°+10°+10° = 3x10° = 30°, and 30°/3 = 10°.
I think that an angle multiplied or divided by a constant (a real number) should be an angle.

My guess would be ambiguity in interpreting the real number -- is it a constant multiplier, or did the user intentionally leave off the ° but still wanted to actually have degrees. In the former, you have what is essentially the same as your examples; in the latter, you would be looking at fractions of a circle (as one possible interpretation). With addition, there is only one reasonable meaning of something like 30° + 3. The only thing you can really add to angle (or perhaps temperature) is a change in angle (temp), so there is no ambiguity.
(04-01-2016 01:12 AM)Claudio L. Wrote: [ -> ]It seems we think alike. Your tags are pretty much the same thing I'm proposing.
The only difference is my proposal of keeping the format of the first argument.
My logic is a bit different, I think of operations as something applied to the number that's on the stack. For example, you have a hex number and you want to increase it. For easy typing, you just want to type 1 +, but you still want the result to be a hex number, hence keeping the first argument format is useful. Same concept helps with temperatures, etc.

For angles, I think the same applies: if you have an angle, it's reasonable to assume if you add a number to it you are adding another angle and want the result to be an angle, you just don't type it as an angle to save keystrokes.
However, when you have a real number and you add an angle, I'm not sure you are working with numbers or angles, so the type should remain "unknown"= real number. At least that's my logic, or it was until I read your post.
I think your logic makes sense too, but I have to think a little more before I break the consistency with all the other parts of newRPL.

Out of interest, what should the calculations 90D 1 + and 1 90D + give in radian mode? On my calculator, 90D 1 + gives 147D and 1 90D + gives 2.57R. In each case, the "1" is treated as 1 radian and the tag for the answer is taken from the first argument. I agree that other approaches can make sense; however, I think it is very desirable that addition should remain commutative. In degrees mode your suggested logic respects this - 91D and 91 are the same quantity, so far as trig functions are concerned. So long as the same is true in radian mode, I'll be content.

Nigel (UK)
(04-01-2016 05:48 PM)Han Wrote: [ -> ]
(04-01-2016 05:33 PM)Helix Wrote: [ -> ]Why?
For me, 10°+10°+10° = 3x10° = 30°, and 30°/3 = 10°.
I think that an angle multiplied or divided by a constant (a real number) should be an angle.

My guess would be ambiguity in interpreting the real number -- is it a constant multiplier, or did the user intentionally leave off the ° but still wanted to actually have degrees. In the former, you have what is essentially the same as your examples; in the latter, you would be looking at fractions of a circle (as one possible interpretation). With addition, there is only one reasonable meaning of something like 30° + 3. The only thing you can really add to angle (or perhaps temperature) is a change in angle (temp), so there is no ambiguity.

Yes, that was my reasoning, but his point is valid, so is Nigel's.

30° + 3 for example. In my view, the user is using degrees and therefore the 3 is in degrees, just saving keystrokes. In Nigel's opinion the 3 is in the current angle system, if in radians then you are adding 3 radians, not 3 degrees.

30°/3 also, in Helix view, the 3 is just a number, in my view the 3 is something unknown, perhaps a time, and then the result is perhaps a speed in degrees per second, hence no longer just an angle.

So I guess we have to find a way to break the ambiguity or live with it. Perhaps keep the first argument also should work for multiplication and division. If you are working on an angle and you divide it (or multiply) by 2, then it's still an angle. If you are working on a number and multiply by an angle, you should get numbers. That way the user controls the output format just by swapping arguments (and of course inverting or negating to get the right answer).
Now multiplication of angle * angle --> number, and also angle/angle --> number. Powers always give numbers, as well as 1/x.
I can see it being useful in geometry (or for surveyors) for example to bisect an angle in DMS format simply by dividing it by 2 (and here we need Helix's way or the first argument trick to make it work).
(04-01-2016 05:48 PM)Han Wrote: [ -> ]
(04-01-2016 05:33 PM)Helix Wrote: [ -> ]Why?
For me, 10°+10°+10° = 3x10° = 30°, and 30°/3 = 10°.
I think that an angle multiplied or divided by a constant (a real number) should be an angle.

My guess would be ambiguity in interpreting the real number -- is it a constant multiplier, or did the user intentionally leave off the ° but still wanted to actually have degrees. In the former, you have what is essentially the same as your examples; in the latter, you would be looking at fractions of a circle (as one possible interpretation). With addition, there is only one reasonable meaning of something like 30° + 3. The only thing you can really add to angle (or perhaps temperature) is a change in angle (temp), so there is no ambiguity.

I think that it is so useful to be able to divide an angle by two, or three (etc.) that division (or multiplication) by a real number should leave the result as an angle. If the intent is to divide one angle by another then the divisor can be explicitly tagged as an angle.

So 30D 3 / gives 10D; 30D 3D / gives 10.

I agree that 30D 3 / is ambiguous - does 3 mean "3" or "3D" or "3R" in radians mode? - but I think that this is the best way to resolve the ambiguity.

Nigel (UK)
(04-01-2016 06:20 PM)Nigel (UK) Wrote: [ -> ]...
In degrees mode your suggested logic respects this - 91D and 91 are the same quantity, so far as trig functions are concerned. So long as the same is true in radian mode, I'll be content.

Only one final observation: The reason to assume the quantity is in the same system as the angle in the stack is two-fold:
a) So that an angle is also just a number (the only reason to have this new way to input angles instead of using units is because angular units cannot be made consistent with non-dimensional quantities and yet preserve its unit designation). Unless we know for sure a number is an angle, let's not assume that on behalf of the user. When there's ambiguity, they behave like raw numbers.
b) To make results independent of system-wide flags. I always want to remove system-wide flag dependency if possible. In this case is not possible nor desirable to eliminate the system-wide angular system. However, a program << ∡90° 1 +>> should produce always the same result regardless of flags state.
(04-01-2016 06:27 PM)Nigel (UK) Wrote: [ -> ]So 30D 3 / gives 10D; 30D 3D / gives 10.

I agree that 30D 3 / is ambiguous - does 3 mean "3" or "3D" or "3R" in radians mode? - but I think that this is the best way to resolve the ambiguity.

I agree on this. On my view:
  • angle +/- scalar = scalar +/- angle = angle, with scalar meaning an angle in the same units as the other operand (30D + 1 = 31D, no matter the current angle mode)
  • angle * scalar = scalar * angle = angle, in the same units as the angular argument
  • angle / scalar = angle, in the same units as the angular operand
  • scalar / angle = scalar, the result is scalar/(angle converted to radians), 2 / 90D = 4/pi
  • angle * angle = scalar, the result being the product of the 2 angles converted to radians (90D * 100G = pi^2/4)
  • angle / angle = scalar, prior to the division, both angles are converted to the same unit (90D / 100G = 1)
  • unary minus: maintains the angle unit
  • trigs: work as expected (tagged arguments are taken into account, untagged ones are in the units given by the current angle mode)
  • inverse trigs: the result is in the units specified by the current angle mode, but see next
  • all others: if any operand is an angle, it is converted to radians

This way, the angular mode only specifies the format used by the direct trigs if the argument is not tagged, and in the inverse trigs to specify the output format.

Still, there are cases where this approach can give rise to funny/unexpected results. I really enjoy the questions raised by Claudio on this project!
A few consequences of this angle tagging that perhaps I didn't mention before:
a) With proper care, it will be possible to create programs that are system-flag independent.
b) Angles carry the format with them, so they will be displayed in the format they were created, regardless of the system flags.
c) Because of b), the user can work simultaneously in multiple angular systems and view all of them in the stack at once.
d) The user can type polar/spherical/cylindrical vectors and polar complex numbers in any system at any time, regardless of system flags.

And a few other consequences (unintended?) for vectors:
a) Polar vectors exist on their own and can be stored in polar form, same as polar complex numbers. The coordinate system is no longer just a display option, it's carried with the vector.
b) Polar vectors keep their polar status in the stack, regardless of system settings.
c) Coordinate system flags can probably be eliminated, as they would serve no purpose now.
d) Operating on vectors just became more complicated to implement, as polar vectors will need to be converted transparently back and forth as needed during operations.
e) I guess the first argument policy will also apply to polar vectors:
polar + vector = polar
vector + polar = vector
(04-01-2016 03:47 PM)Claudio L. Wrote: [ -> ]
(04-01-2016 02:10 AM)rprosperi Wrote: [ -> ]Caught red-handed! I thought you were referring to "oldRPL" aka the 48/49/50 flavor...

I guess I missed when this was changed in newRPL. I can go back and read why, implications, etc.

I have not flashed with newRPL yet for the obvious reason: so many toys, so little time... I'll catch up eventually. And newRPL will be even better then.

And now I have your written confession... guilty as charged! Bring the guillotine!
On second thought, I'll just write your name in the "users I still need to convince by improving newRPL to perfection" list, and I'll forgive you.

Claudio

Don't worry. After Bob got my presentation of newRpl at the next conference he will rush and buy another HP50G just to dedicate it for unknown terrain to explore Smile

Günter
(04-01-2016 07:01 PM)emece67 Wrote: [ -> ]I agree on this. On my view:
[list]
[*]angle +/- scalar = scalar +/- angle = angle, with scalar meaning an angle in the same units as the other operand (30D + 1 = 31D, no matter the current angle mode)
[*]angle * scalar = scalar * angle = angle, in the same units as the angular argument
[*]angle / scalar = angle, in the same units as the angular operand
So far so good, scalar +/- angle breaks consistency but could be done.
(04-01-2016 07:01 PM)emece67 Wrote: [ -> ][*]scalar / angle = scalar, the result is scalar/(angle converted to radians), 2 / 90D = 4/pi
[*]angle * angle = scalar, the result being the product of the 2 angles converted to radians (90D * 100G = pi^2/4)
This could be a problem. For example an expression:
'ASIN(A)*ASIN(A)/ASIN(A)'. The user has the settings in DEG, and thinks ASIN will return DEG, so you would expect the result to be let's say 45 or ∡45°.
If the operator associates the right side first:
ASIN(A)*(ASIN(A)/ASIN(A)) = ASIN(A)*1 = ∡45°
but if it associates from the left:
(ASIN(A)*ASIN(A))/ASIN(A) = (pi/4)^2/∡45° = pi/4 (correct, but in radians!)

With the "angle is a number" approach:
ASIN(A)*(ASIN(A)/ASIN(A)) = ASIN(A)*1 = ∡45°
(ASIN(A)*ASIN(A))/ASIN(A) = 45^2 / ∡45° = 45 (not an angle, but at least the same number as expected)

On the other hand, the "angle is a number" can also give bad results in expressions with mixed systems:

'∡50g*2/∡90°'

If associated from the left:
(∡50g*2)/∡90° = ∡100g/∡90° = 1
versus:
∡50g*(2/∡90°)= ∡50g*(1/45)= ∡1.11111111111111g

This can seriously drive a user crazy. What could be a solution for this?
(04-01-2016 11:15 PM)Claudio L. Wrote: [ -> ][...]
This could be a problem. For example an expression:
'ASIN(A)*ASIN(A)/ASIN(A)'. The user has the settings in DEG, and thinks ASIN will return DEG, so you would expect the result to be let's say 45 or ∡45°.
If the operator associates the right side first:
ASIN(A)*(ASIN(A)/ASIN(A)) = ASIN(A)*1 = ∡45°
but if it associates from the left:
(ASIN(A)*ASIN(A))/ASIN(A) = (pi/4)^2/∡45° = pi/4 (correct, but in radians!)

With the "angle is a number" approach:
ASIN(A)*(ASIN(A)/ASIN(A)) = ASIN(A)*1 = ∡45°
(ASIN(A)*ASIN(A))/ASIN(A) = 45^2 / ∡45° = 45 (not an angle, but at least the same number as expected)
[...]

Not a flaw I've foreseen, but a flaw in any case. I expect all approaches not enforcing unit consistency (not raising errors with ops like scalar / angle) to have such kinds of flaws.
Pages: 1 2
Reference URL's