TheSwamp

Code Red => AutoLISP (Vanilla / Visual) => Topic started by: domenicomaria on April 22, 2021, 10:53:09 AM

Title: FIX issue
Post by: domenicomaria on April 22, 2021, 10:53:09 AM
(setq x-deg                  12.3456                    )    ; 12.3456
(setq i-deg                   (fix x-deg)                 )   ; 12      
(setq sub-deg               (- x-deg i-deg)           )   ; 0.3456   
(setq sub-deg-10000     (* sub-deg 10000.0)  )   ; 3456.0   
(setq i-sub-deg-10000   (fix sub-deg-10000 )  )   ; 3455 ? ? ?

why ?
Title: Re: FIX issue
Post by: danAllen on April 22, 2021, 11:14:13 AM
On Bricscad v15 I get this:
Code: [Select]
: (setq x-deg 12.3456 )
12.3456
: (setq i-deg (fix x-deg) )
12
: (setq sub-deg (- x-deg i-deg) )
0.345599999999999
: (setq sub-deg-10000 (* sub-deg 10000.0) )
3455.99999999999
: (setq i-sub-deg-10000 (fix sub-deg-10000 ) )
3455

my LUPREC is set to 8, could yours be set to smaller hiding the .99999?
Title: Re: FIX issue
Post by: domenicomaria on April 22, 2021, 11:26:19 AM
my LUPREC is set to 8, too !

But I get what I wrote in my first post !

And I don't understand why.

I get    sub-deg-10000 = 3456.0

but (FIX sub-deg-10000) = 3455 ! ! !  
Title: Re: FIX issue
Post by: ribarm on April 22, 2021, 11:59:23 AM
last line :

try (it won't influence on integer result, but it will fix an issue) :

(setq i-sub-deg-10000   (fix (+ sub-deg-10000 0.01))  )
Title: Re: FIX issue
Post by: CincyJeff on April 22, 2021, 12:58:13 PM
(rtos (* sub-deg 10000.0) 2 10) -> "3456.0000000000"
(rtos (* sub-deg 10000.0) 2 11) -> "3455.99999999999"
You could (fix (atof (rtos...
Title: Re: FIX issue
Post by: domenicomaria on April 22, 2021, 01:41:00 PM
last line :

try (it won't influence on integer result, but it will fix an issue) :

(setq i-sub-deg-10000   (fix (+ sub-deg-10000 0.01))  )

There are some workarounds, but, why this happens ?

Is it a BUG ?
Title: Re: FIX issue
Post by: danAllen on April 22, 2021, 02:18:38 PM
why use FIX instead of a rounding function?

Code: [Select]
;;;==========================================================
;;;Rounding function - Joe Burke - 2/23/03
;;;==========================================================
(defun SAA_round (value to)
   (if (zerop to) value
     (* (atoi (rtos (/ (float value) to) 2 0)) to))
)

: (saa_round 3455.99999999999 1)
3456

see https://www.theswamp.org/index.php?topic=44487.0
Title: Re: FIX issue
Post by: domenicomaria on April 22, 2021, 02:54:17 PM
Quote
(saa_round 3455.99999999999 1) -> 3456

the problem is NOT to ROUND a number.

The problem is WHY this happens ?


I have a simple math problem.

And I want to solve it with math.

And I don't want to use strings.

Not possible in VLisp?

Title: Re: FIX issue
Post by: domenicomaria on April 22, 2021, 02:59:44 PM
Code - Auto/Visual Lisp: [Select]
  1. (defun :GGMMSS>RAD (x-ggmmss / x-gg x-ggmmss-lst x-ggmmss-s x-mm x-mmss x-ss)
  2.         (setvar "dimzin" 0)
  3.         (setq   x-ggmmss-s      (rtos x-ggmmss 2 4)
  4.                 x-ggmmss-lst    (LM:str->lst x-ggmmss-s ".")
  5.                 x-gg            (read (nth 0 x-ggmmss-lst) )
  6.                 x-mmss          (nth 1 x-ggmmss-lst)
  7.                 x-mm            (read (substr x-mmss 1 2) )
  8.                 x-ss            (read (substr x-mmss 3 2) )                            
  9.         )
  10.         (/ (*   (+ x-gg (/ x-mm 60.0) (/  x-ss 3600.0) ) pi) 180.0)
  11. )
  12.  

I need to convert degrees in sexagesimal format to radians.
And this function works well.

But I used strings.

Is there a mathematical-only solution in VLISP?
Title: Re: FIX issue
Post by: domenicomaria on April 22, 2021, 05:12:08 PM
anyway this works :

Code - Auto/Visual Lisp: [Select]
  1. (setq   ggmmss          123.4567
  2.         gg              (float (fix ggmmss) )
  3.         mmss            (- ggmmss gg)
  4.         mmss-100        (* mmss 100.0)
  5.         mm              (float (fix mmss-100) )
  6.         ss              (- mmss-100 mm)
  7.         ss              (* ss 100.0)
  8. )
  9. (list gg mm ss)
Title: Re: FIX issue
Post by: Lee Mac on April 22, 2021, 05:41:18 PM
It is worth noting that issues such as this are not specific to AutoLISP or indeed any programming language, but rather apply to computing in general.

This issue arises because a computer has finite memory in which data may be stored, and consequently not all numbers can be represented exactly, but instead must be approximated and stored in an agreed format (specifically, in this case, the double-precision floating-point format (https://en.wikipedia.org/wiki/Double-precision_floating-point_format), or simply 'double') which has inherent compromises at the extent of its precision in order to be useful under a wide range of applications (i.e. affording an ability to represent both very large numbers (to an order of magnitude of 1e308) with low precision, and very small numbers with high precision).

Just as one third cannot be represented exactly in base 10 [decimal] (instead yielding the infinitely recurring decimal 0.333...10) but can be represented exactly in base 3 (as 0.13), there are many numbers which equally cannot be represented exactly in base 2 [binary], and indeed, the same would arise for any chosen base.

As a result of this hard limit on the level of precision to which any number may be stored, arithmetic errors (https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems) begin to creep into calculations where rounding occurs at the very limit of precision for almost every calculation, and such infinitesimal errors propagate with repeated & compound calculations.

In your particular example, we see that performing the subtraction of the integer from the double, followed by a multiplication by the double 10000.0 introduces an infinitesimal rounding error at the limit of precision of the double, which may be observed by converting the double to a string using the rtos function:

Code - Auto/Visual Lisp: [Select]
  1. _$ (rtos sub-deg-10000 2 20)
  2. "3455.999999999992"

Now, since the fix function merely removes the fractional component of the data to yield an integer value, the introduction of this rounding error will cause the function to return 3455 instead of 3456.

As such, when working with doubles, we must always account for the possibility of rounding and always include a 'fuzz factor' or tolerance when performing any operation which will either attempt to yield an exact value (such as the use of the fix function), or compare a double with an exact value (such as you might with the = operator when comparing two integers).

In this case, I would suggest the following (you may decide to opt for a smaller tolerance, such as 1e-12):
Code - Auto/Visual Lisp: [Select]
  1. _$ (fix (+ sub-deg-10000 1e-8))
  2. 3456

Or, where a comparison is concerned, the use of the equal function with an appropriate tolerance:
Code - Auto/Visual Lisp: [Select]
  1. _$ (equal 3456 sub-deg-10000 1e-8)
  2. T
Title: Re: FIX issue
Post by: danAllen on April 22, 2021, 11:53:48 PM
Also I believe AutoLisp only holds 16 digits of significance for real numbers. This can be shown by progressively adding a tens digit to the pi. (ten power?)
Code: [Select]
: (setq i 1)
1
: (repeat 16 (princ (strcat "\n" (rtos (+ (setq i (* i 10)) pi) 2 32))))

13.14159265358979
103.1415926535898
1003.141592653590
10003.14159265359
100003.1415926536
1000003.141592654
10000003.14159265
100000003.1415927
1000000003.141593
10000000003.14159
100000000003.1416
1000000000003.142
10000000000003.14
100000000000003.1
1000000000000003
1.000000000000000

Even though RTOS is being asked to show 32 digits of significance, it only shows 16.

Further by adding increasing 10, 100, 1000 etc to pi, then subtracting, significant decimal numbers are lost

Code: [Select]
: (setq i 1)
1
: (repeat 16 (princ (strcat "\n" (rtos (- (+ (setq i (* i 10)) pi) i) 2 32))))

3.141592653589793
3.141592653589797
3.141592653589782
3.141592653590124
3.141592653584667
3.141592653584667
3.141592653468251
3.141592651605606
3.141592621803284
3.141592025756836
3.141586303710938
3.141601562500000
3.140625000000000
3.140625000000000
3.125000000000000
4.000000000000000

(I'm probably missing something mathematically, but just wanted to make the point that CAD has a limit to precision)
Title: Re: FIX issue
Post by: domenicomaria on April 23, 2021, 05:44:10 AM
Quote
This issue arises because a computer has finite memory in which data may be stored, and consequently not all numbers can be represented exactly, but instead must be approximated and stored in an agreed format (specifically, in this case, the double-precision floating-point format, or simply 'double') which has inherent compromises at the extent of its precision in order to be useful under a wide range of applications (i.e. affording an ability to represent both very large numbers (to an order of magnitude of 1e308) with low precision, and very small numbers with high precision).

I know this problem, well.

And for that I think someone should implement another version
of the FIX command with limitations.

But within these limits, the command should work well.

The limitations could be the size of the number and the number of digits.

it is not elegant and acceptable that
to get the integer part of a number,
one must first convert it to a string,
and then divide it and take the part before the point
and then use the READ command . . .

. . . it is not acceptable !

Title: Re: FIX issue
Post by: Marc'Antonio Alessi on April 23, 2021, 10:29:40 AM

I need to convert degrees in sexagesimal format to radians.
Is there a mathematical-only solution in VLISP?
Sexagesimal format is : 123°34'56"   :-o
Title: Re: FIX issue
Post by: danAllen on April 23, 2021, 10:56:08 AM
Rereading the first post, I see the real question is why does just subtracting an integer result in repeating decimal?
Code: [Select]
: (- 12.3456 12)
0.345599999999999
: (- 12.3456 12.0)
0.345599999999999
: (- 12.3 12)
0.300000000000001
Title: Re: FIX issue
Post by: Marc'Antonio Alessi on April 23, 2021, 11:26:43 AM
Code: [Select]
: (setq Prec 100000)
: (/ (- (* Prec 12.3456) (* Prec 12)) Prec)  => 0.3456

: (setq Prec 10000)
: (/ (- (* Prec 12.3456) (* Prec 12)) Prec) => 0.345599999999999
Title: Re: FIX issue
Post by: Lee Mac on April 23, 2021, 02:08:51 PM
And for that I think someone should implement another version
of the FIX command with limitations.

But within these limits, the command should work well.

Code - Auto/Visual Lisp: [Select]
  1. (defun fix2 ( x ) (fix (+ x 1e-8)))
Title: Re: FIX issue
Post by: Lee Mac on April 23, 2021, 02:12:57 PM
Also I believe AutoLisp only holds 16 digits of significance for real numbers.

It is worth noting that this is an inherent limitation of the double-precision floating-point format (https://en.wikipedia.org/wiki/Double-precision_floating-point_format) used by AutoLISP to store the 'real' data type, not specifically a limitation of AutoLISP:
Quote from: https://en.wikipedia.org/wiki/Double-precision_floating-point_format
The 53-bit significand precision gives from 15 to 17 significant decimal digits precision (2−53 ≈ 1.11 × 10−16).
Title: Re: FIX issue
Post by: Lee Mac on April 23, 2021, 02:16:16 PM
I need to convert degrees in sexagesimal format to radians.

Have you considered the angtof (http://help.autodesk.com/view/ACD/2022/ENU/?guid=GUID-9DE9200B-F27E-4BB6-8AE6-BC5C8A64A527) function?
Title: Re: FIX issue
Post by: domenicomaria on April 23, 2021, 04:45:05 PM
I need to convert degrees in sexagesimal format to radians.

Have you considered the angtof (http://help.autodesk.com/view/ACD/2022/ENU/?guid=GUID-9DE9200B-F27E-4BB6-8AE6-BC5C8A64A527) function?

(defun :GGMMSS>RAD   (ggmmss / gg mm mmss mmss-100 ss)
   (setq   gg        (float (fix ggmmss) )
           mmss      (- ggmmss gg)
           mmss-100  (* mmss 100.0)
           mm        (float (fix mmss-100) )
           ss        (- mmss-100 mm)
           ss        (* ss 100.0)
   )
   (/ (*   (+ gg (/ mm 60.0) (/  ss 3600.0) )   pi) 180.0)
)

123.4567 is a SEXAGESIMAL angle  123° 45' 67''

I want convert it in RADIANTS

(:GGMMSS>RAD 123.4567) =  2.16017   (2.1601697745093262)


- - - - - - - - - - - - - - - - - - - - -

(ANGTOF string [units])
0 - Degrees
1 - Degrees/minutes/seconds
2 - Grads
3 - Radians
4 - Surveyor's units


I don't know how to FORMAT the INPUT for ANGTOF

(ANGTOF "123.4567" 3) = 4.07618

but it is a WRONG RESULT !
Title: Re: FIX issue
Post by: d2010 on April 23, 2021, 04:54:38 PM
Can you execute my lisp?>
:yes:
Code: [Select]
(Defun dfn_real_rtos (x151 unitstar / rr a b od r dz x)
  (setq;|a580492|;
x x151
unitstar (if (numberp unitstar) (abs unitstar) (getvar "LUNITS"))
dz "DIMZIN") (if (or  (=  unitstar 4) (=  unitstar 5)) (setq;|a580602|;
$rr (rtos x)) (progn  (setq;|a580642|;
od (getvar dz)) (setvar dz (boole 1  od (~ 8))) (setq;|a580694|;
a (rtos x)) (setvar dz (boole 7  od 8)) (setq;|a580744|;
b (rtos x unitstar 15)) (setvar dz od) (setq;|a580804|;
$rr (if (equal (distof a) (distof b) 0.000001) a b))))
$rr)

Title: Re: FIX issue
Post by: Lee Mac on April 23, 2021, 07:01:43 PM
(ANGTOF string [units])
0 - Degrees
1 - Degrees/minutes/seconds
2 - Grads
3 - Radians
4 - Surveyor's units

I don't know how to FORMAT the INPUT for ANGTOF

(ANGTOF "123.4567" 3) = 4.07618

but it is a WRONG RESULT !

You have not read the documentation for the angtof function that I linked above - the function will always return a value in radians and the units argument represents the format of the supplied string, not the output.
Code - Auto/Visual Lisp: [Select]
  1. _$ (rtos (angtof "123d45'67\"" 1) 2 15)
  2. "2.160169774509326"
Title: Re: FIX issue
Post by: danAllen on April 23, 2021, 08:49:56 PM
It is worth noting that this is an inherent limitation of the double-precision floating-point format (https://en.wikipedia.org/wiki/Double-precision_floating-point_format) used by AutoLISP to store the 'real' data type, not specifically a limitation of AutoLISP:
Quote from: https://en.wikipedia.org/wiki/Double-precision_floating-point_format
The 53-bit significand precision gives from 15 to 17 significant decimal digits precision (2−53 ≈ 1.11 × 10−16).

trying to understand why (- 12.3456 12) = 0.345599999999999

I searched for "convert decimal to Double-precision floating point" and found this:
https://www.h-schmidt.net/FloatConverter/IEEE754.html (https://www.h-schmidt.net/FloatConverter/IEEE754.html)

entering 12.3456 & 12 gets the following. I don't understand it but now I have a little insight into why the math acts strangely.




Title: Re: FIX issue
Post by: domenicomaria on April 24, 2021, 01:24:37 AM
Quote
Lee Mac :
You have not read the documentation for the angtof function that I linked above -
the function will always return a value in radians
and the units argument represents the format of the supplied string,
not the output.


(rtos (ANGTOF "123d45'67\"" 1) 2 15) = "2.160169774509326"


Ok.
But what have I to do to FORMAT the INPUT for ANGTOF  ?

ANGTOS returns the following results,


(ANGTOS 123.4567 0) = "233.548"
(ANGTOS 123.4567 1) = "233d32'52\""
(ANGTOS 123.4567 2) = "259.498g"
(ANGTOS 123.4567 3) = "4.076r"
(ANGTOS 123.4567 4) = "S 36d27'8\" W"

and never returns what i need : "123d45'67\""
. . .

If it required that I have to produce "123d45'67\""  "manually"
it means that I have before to extract the degrees, the minutes and the seconds . . .
convert them into strings and at the end,
format it in this way : "123d45'67\""
. . .
but at this point, in this situation,
(a number like this 123.4567, that means 123° 45' 67'')
(I acquire this data from a surveying instrument, in this format)
ANGTOS and ANGTOF are not useful . . .

and I prefer to use my well working :GGMMSS>RAD

GGMMSS>RAD doesn't use any conversion to string.

And this is RIGHT for a MATH issue.
Title: Re: FIX issue
Post by: domenicomaria on April 24, 2021, 01:49:37 AM
Code: [Select]
: (setq Prec 100000)
: (/ (- (* Prec 12.3456) (* Prec 12)) Prec)  => 0.3456

: (setq Prec 10000)
: (/ (- (* Prec 12.3456) (* Prec 12)) Prec) => 0.345599999999999

interesting !
Title: Re: FIX issue
Post by: domenicomaria on April 24, 2021, 01:50:43 AM
Code - Auto/Visual Lisp: [Select]
  1. (defun fix2 ( x ) (fix (+ x 1e-8)))

Interseting !
Title: Re: FIX issue
Post by: VovKa on April 24, 2021, 10:38:40 AM
(a number like this 123.4567, that means 123° 45' 67'')
(I acquire this data from a surveying instrument, in this format)
is this data stored in a binary file?
Title: Re: FIX issue
Post by: domenicomaria on April 24, 2021, 01:50:20 PM
(a number like this 123.4567, that means 123° 45' 67'')
(I acquire this data from a surveying instrument, in this format)
is this data stored in a binary file?
it is stored in an ascii file, a simple txt file
Title: Re: FIX issue
Post by: domenicomaria on April 24, 2021, 04:08:05 PM
reading your comments, i understand that i didn't explain well what the problem is.

I have a text file that contains a set of data representing a points cloud.

The used (cheap, Chinese, but still reliable and accurate) topographic instrument is configured to return
degrees in sexagesimal format.

But these degrees in sexagesimal format
are written NOT in the true standard sexagesimal format.

But they are written in an APPARENT decimal format.

Then, in this situation,
123.4567
means
123° 45' 67''

And since I need to convert it to radians,
first I have to extract the degrees, minutes and seconds.

Then apply the conversion formula from sexagesimal to decimal degrees
degrees + minutes/60 + seconds/3600
and then convert these degrees from decimal format to radians.

I realize that due to the particularity of the initial format,
everything can be misunderstood.

I hope I have explained well.

And thank you all for the suggestions you give me.

ciao
Title: Re: FIX issue
Post by: danAllen on April 24, 2021, 04:39:37 PM
does this work?
Code: [Select]
(defun sgf_convert (n / )
  (+ (atoi (substr n 1 (vl-string-search "." n)))
     (/ (atoi (substr n (+ 2 (vl-string-search "." n)) 2)) 60.0)
     (/ (atoi (substr n (+ 4 (vl-string-search "." n)) 2)) 3600.0)
  )
)
(defun dtr (a)(* pi (/ a 180.0)))

(sgf_convert "123.4567") -> 123.768611111111
(dtr (sgf_convert "123.4567")) -> 2.16016977450933

assumes always 4 digits after decimal, and since you are reading from a file you are starting with a string
Title: Re: FIX issue
Post by: BIGAL on April 24, 2021, 11:07:11 PM
Just a ps degs max 360, Minutes max 60, seconds max 60, can not have 67.
Title: Re: FIX issue
Post by: domenicomaria on April 25, 2021, 01:48:14 AM
Just a ps degs max 360, Minutes max 60, seconds max 60, can not have 67.
yes
you are right !


I have not taken this number from the txt file of the surveying instrument.

I used this number to give an example (wrong)
. . . if you notice, the digits of this number are increasing digits
1 2 3 4 5 6 7. . .

I only used it to give an example and didn't consider that I couldn't use 67 for the seconds!


Title: Re: FIX issue
Post by: domenicomaria on April 25, 2021, 02:10:26 AM
does this work?
Code: [Select]
(defun sgf_convert (n / )
  (+ (atoi (substr n 1 (vl-string-search "." n)))
     (/ (atoi (substr n (+ 2 (vl-string-search "." n)) 2)) 60.0)
     (/ (atoi (substr n (+ 4 (vl-string-search "." n)) 2)) 3600.0)
  )
)
(defun dtr (a)(* pi (/ a 180.0)))

(sgf_convert "123.4567") -> 123.768611111111
(dtr (sgf_convert "123.4567")) -> 2.16016977450933

assumes always 4 digits after decimal, and since you are reading from a file you are starting with a string

Yes

It works well.
I prefer to not use strings.

But it works.
Bravo !
Title: Re: FIX issue
Post by: domenicomaria on April 25, 2021, 05:26:02 AM
does this work?
Code: [Select]
(defun sgf_convert (n / )
  (+ (atoi (substr n 1 (vl-string-search "." n)))
     (/ (atoi (substr n (+ 2 (vl-string-search "." n)) 2)) 60.0)
     (/ (atoi (substr n (+ 4 (vl-string-search "." n)) 2)) 3600.0)
  )
)
(defun dtr (a)(* pi (/ a 180.0)))

(sgf_convert "123.4567") -> 123.768611111111
(dtr (sgf_convert "123.4567")) -> 2.16016977450933

assumes always 4 digits after decimal, and since you are reading from a file you are starting with a string

DanAllen your code with some little modifications . . .

Code - Auto/Visual Lisp: [Select]
  1. (defun DDMMGG>RAD (ang / ang-str dot-pos)
  2.         (setvar "dimzin" 0)
  3.         (if(numberp ang) (setq ang-str (rtos ang 2 4) ) (setq ang-str ang) )
  4.         (setq dot-pos (vl-string-search "." ang-str))
  5.         (* pi
  6.                 (/
  7.                         (+      (atoi (substr ang-str 1 dot-pos))
  8.                                 (/ (atoi (substr ang-str (+ 2 dot-pos) 2)) 60.0)
  9.                                 (/ (atoi (substr ang-str (+ 4 dot-pos) 2)) 3600.0)
  10.                         )
  11.                         180.0
  12.                 )
  13.         )
  14. )
  15.  

(rtos (DDMMGG>RAD 222.3344) 2 15)
3.884443568333069
Title: Re: FIX issue
Post by: danAllen on April 25, 2021, 11:04:55 AM
I prefer to not use strings.

But it works.
Bravo !

Thank you. I'm glad I could help.

A minor quibble - you said the data was stored in an ascii file, a simple txt file. I'm pretty sure that means when it is read from file, the original format is a string. It is a string representing three numbers, the first is from 1 to 3 digits (or does it encode "001.2345"?) , a "." separator, then two numbers from 0 to 59, encoded with leading 0 (ex "01" or "59") - and no separator.
To convert it to decimal then back again with the DDMMGG>RAD function seems like an extra step and led to some of the confusion about what you needed to accomplish.
Title: Re: FIX issue
Post by: domenicomaria on April 25, 2021, 11:59:24 AM
danAllen,
I read a TXT file, and get a LIST of STRINGS.

Every string contains 7 information, separated from comma (",")
So I split every string in its 7 sub-strings
and then I READ every item.

2 of this seven items are the horizontal angle and the vertical angle.

They rappresent angles in SEXAGESIMAL FORMAT.

But they are formatted as DECIMAL DEGREES.

So I need to get the number before the dot
and after,
i have to get the first 2 numbers and the second 2 numbers
(after the dot)
that rappresent the minutes and the seconds.

You know the rest.

That's all.

Ciao