v2.93 BUG! - Places: angle conversion is flawed [FIXED ±]
Verfasst: 11.02.2016, 23:53
Dirk,
I noticed in the Navigator text for Joseph Becker (in the Demo.ahn file), the latitude - expressed as a decimal degrees - is incorrect (30' is always 0.5 degrees). In the Places Management dialog, the number is correct (if I remember), but displays wrongly in the Navigator. I am assuming the problem lies with the conversions between decimal degrees and degrees-minutes-seconds. Actually, from decimal degrees to DMS is not as simple as it appears on the surface.
I have a small Pascal test file (following) which demonstrates the routines necessary. I hope the code is simple enough to follow.
Also, in the Navigator the location is displayed as decimal degrees. I think that method is not commonly used anywhere - decimal degrees are used in computation only (as a rule).
Here's a better way to do the same think (above is quite "klunky"):
Followup: I had a closer look at the value displayed in the Navigator's data box (for Joseph Becker)- the latitude is displayed as 51.516666 (for 51°30'). Converting back to DMS yields 51°30'60"! This is a classic rounding error in computers. I'm guessing the conversion routines currently use the INT function to grab the degrees or minutes, etc., but INT returns a real (from a real). All reals are encoded using base 2 techniques and are subject to tiny rounding errors - using INT (working with reals again) can produce a false result. For example, somewhere in your routine, the code may take the INT of 30.99999.... and returns 31, since the source is subject to error and the INT contributes to that error by again working in base 2 reals. I recommend having a look at my second routine - it should be fairly "bullet-proof", at least until the 5th (or higher) decimal place of seconds. The latitude and longitude can be stored internally as EXTENDED reals (10 bytes, less handling) then converted back to DMS when needed; there shouldn't be a problem with that. By the way, I tried deleting the Dortmund latitude in Places Management, then re-entering it - seemed to convert OK. But why does Places display (on opening the file) 51.5, but the Navigator box displays 51.56666?
- Mathemagician (Ruhestand professionellen Landvermesser)
P.S. In my career, we ALWAYS used Hewlett-Packard calculators and computers. Mostly (in the old days!) they had proprietary CPUs, working in BCD (binary coded decimal). NO rounding errors!
I noticed in the Navigator text for Joseph Becker (in the Demo.ahn file), the latitude - expressed as a decimal degrees - is incorrect (30' is always 0.5 degrees). In the Places Management dialog, the number is correct (if I remember), but displays wrongly in the Navigator. I am assuming the problem lies with the conversions between decimal degrees and degrees-minutes-seconds. Actually, from decimal degrees to DMS is not as simple as it appears on the surface.
I have a small Pascal test file (following) which demonstrates the routines necessary. I hope the code is simple enough to follow.
Also, in the Navigator the location is displayed as decimal degrees. I think that method is not commonly used anywhere - decimal degrees are used in computation only (as a rule).
Code: Alles auswählen
Program conversiontest;
VAR
{Note: functions require numeric values, string -> numeric may be required,
as well as numeric -> string for display, depending on your usage}
{variables for testing. Using globals for clarity}
deg : extended ; {decimal degrees}
d : longint ; {degrees - could be negative for negative angles}
m : longint ; {minutes}
s : extended ; {seconds - might be fractions of a second, hence extended}
{------------------------------}
PROCEDURE dms2deg ; {convert degrees-minutes-seconds to decimal degrees}
VAR
isneg : boolean ;
temp : longint ;
BEGIN
IF d < 0
THEN isneg := true
ELSE isneg := false ; {save sign}
temp := abs (d) ;
deg := temp+((m+(s/60))/60) ;
IF isneg
THEN deg := -deg
END ; {dms2deg}
{------------------------------}
PROCEDURE deg2dms ; {convert decimal degrees to degrees-minutes-seconds}
{A bit trickier, due to "rollover" during conversion, i.e. if result yields
60 seconds, that is actually 1 minute + 0 seconds!}
VAR
temp : extended ; {MUCH easier to work in seconds!}
absdeg : extended ; {absolute value of input}
isneg : boolean ;
BEGIN
IF (deg < 0)
THEN isneg := true
ELSE isneg := false ;{save sign}
absdeg := abs (deg) ; {only work with positive angles}
d := trunc (absdeg) ; {get degrees}
temp := absdeg * 3600 ; {convert absolute value to seconds}
temp := temp - (d * 3600) ; {remainder, in seconds}
{Note: d is longint, else * 3600 could cause 'rollover' in 16-bit integer}
m := trunc (temp/60) ; {get minutes}
s := temp - (m * 60) ; {get remainder, in seconds and decimal seconds}
IF isneg
THEN d := -d ; {might be negative}
END ; {deg2dms}
{------------------------------}
BEGIN {Lets test it out!}
d := 63 ; {assign test data}
m := 59 ; {CANNOT be negative in the real world}
s := 55.125 ; {CANNOT be negative in the real world}
dms2deg ;
write ('D:',d,' ','M:',m,' ','S:',s:0:3) ; {seconds to 3 decimals}
writeln (' -> DEG:',deg:0:8) ; {decimal degrees - 8 decimals}
writeln ;
deg2dms ; {just use answer from previous conversion}
write ('DEG:',deg:0:8) ;
writeln (' -> D:',d,' ','M:',m,' ','S:',s:0:3) ; {seconds to 3 decimals}
END.
Code: Alles auswählen
Program conversiontest1;
VAR
{Note: functions require numeric values; normal conversions to/from strings
may be required, depending on your usage. This approach is a little
more 'modern', since it uses FUNCTIONS/PROCEDURES with parameters
passed, rather than global variables. Also, the FRAC function is used
to bypass a step or two. The routines also work for negative angles,
as did the first routine ("conversiontest").}
{variables for testing}
deg : extended ; {decimal degrees}
d : longint ; {degrees - could be negative for negative angles}
m : longint ; {minutes}
s : extended ; {seconds - might be fractions of a second, hence extended}
{------------------------------}
FUNCTION dms2deg (_d,_m:longint; _s:extended): extended ; {DMS to dec. deg}
{Convert degrees-minutes-seconds to decimal_degrees. Always work with
positive input.}
VAR
isneg : boolean ;
temp : longint ;
decdeg : extended ;
BEGIN
IF _d < 0
THEN isneg := true
ELSE isneg := false ; {save sign}
temp := abs (_d) ;
decdeg := temp+((_m+(_s/60))/60) ;
IF isneg
THEN dms2deg := -decdeg {make negative}
ELSE dms2deg := decdeg
END ; {dms2deg}
{------------------------------}
PROCEDURE deg2dms (_deg:extended; VAR _d,_m:longint; VAR _s:extended) ;
{Convert decimal_degrees to degrees-minutes-seconds. Always work with
positive input.}
VAR
isneg : boolean ;
absdeg : extended ; {absolute value of input}
temp : extended ; {remainders}
BEGIN
IF (_deg < 0)
THEN isneg := true
ELSE isneg := false ; {save sign}
absdeg := abs (_deg) ; {only work with positive angles}
_d := trunc (absdeg) ; {get degrees}
temp := frac (absdeg) * 60 ; {remainder = decimal_minutes}
_m := trunc (temp) ; {get minutes}
_s := frac (temp) * 60 ; {get seconds}
IF isneg
THEN _d := -d ; {return negative sign}
END ; {deg2dms}
{------------------------------}
BEGIN {Lets test it out!}
d := 63 ; {assign test data}
m := 59 ; {CANNOT be negative in the real world}
s := 55.125 ; {CANNOT be negative in the real world}
deg := dms2deg (d,m,s) ;
write ('D:',d,' ','M:',m,' ','S:',s:0:3) ; {seconds to 3 decimals}
writeln (' -> DEG:',deg:0:8) ; {decimal degrees - 8 decimals}
writeln ;
{Lets make SURE the routine works, by zeroing the output first}
d := 0 ; m := 0 ; s := 0 ;
{Then calculate d/m/s from "scratch"}
deg2dms (deg,d,m,s) ; {just use answer from previous conversion}
write ('DEG:',deg:0:8) ;
writeln (' -> D:',d,' ','M:',m,' ','S:',s:0:3) {seconds to 3 decimals}
END.
- Mathemagician (Ruhestand professionellen Landvermesser)
P.S. In my career, we ALWAYS used Hewlett-Packard calculators and computers. Mostly (in the old days!) they had proprietary CPUs, working in BCD (binary coded decimal). NO rounding errors!