Visual LISP, AutoLISP and General Customization
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Strange behavior from polar function in LISP

13 REPLIES 13
SOLVED
Reply
Message 1 of 14
BigDumbWeirdo
967 Views, 13 Replies

Strange behavior from polar function in LISP

Hi all. I was wondering if anyone else had encountered this, and if so, if anyone knows what's going on and why.

I've been writing a command that uses the polar function, but during debugging I'm noticing some strange behavior. Specifically, when I use the polar function with the 0,0 point of the drawing as input and any right angle other than 0, it gives a point just slightly off from where it should be. Here's some examples:

Command: (polar '(0 0 0) pi 1)
(-1.0 1.22465e-016 0.0)

 

Command: (polar '(0 0 0) (* pi 0.5) 1)
(6.12323e-017 1.0 0.0)

 

Command: (polar '(0 0 0) (* pi 1.5) 1)
(-1.83697e-016 -1.0 0.0)

 

It doesn't happen when the angle is 0, or if the angle is not perpendicular or parallel to 0 (such as a 45 degree angle, or a 167 degree angle). It looks to me like it might be some discrepency between the precisions used in angles and those used in other floating point numbers, but I'm not sure. Does anyone else have any insight into this?

13 REPLIES 13
Message 2 of 14
dbroad
in reply to: BigDumbWeirdo

I know this perhaps isn't going to help your pain but its just part of learning the results of the program.  Since PI is irrational and since the CPU has limits on its accuracy, you will see slight variations.  For all intents, your odd looking numbers are extremely close to the target values (0).  So you should use the equal function rather than the = function to test, and use the tolerance option.

If you need exactly 0, then you could apply a function to ensure it.

 

(setq test '(-1.0 1.22465e-016 0.0))

 

(mapcar '(lambda (x) (if (equal x 0 1e-15) 0.0 x)) test)

should return (-1.0 0.0 0.0)

 

Architect, Registered NC, VA, SC, & GA.
Message 3 of 14
BigDumbWeirdo
in reply to: dbroad

That's what I thought it was, although I didn't quite phrase it right in the OP.
So long as it's beyond the level of precision autocad uses, I suppose it's not a problem.
Message 4 of 14
dbroad
in reply to: BigDumbWeirdo

Unfortunately that is what we must deal with.  I edited my first post to demonstrate a workaround if interested.

Architect, Registered NC, VA, SC, & GA.
Message 5 of 14
Kent1Cooper
in reply to: BigDumbWeirdo


@BigDumbWeirdo wrote:

....

I've been writing a command that uses the polar function, but during debugging I'm noticing some strange behavior. Specifically, when I use the polar function with the 0,0 point of the drawing as input and any right angle other than 0, it gives a point just slightly off from where it should be. Here's some examples:

Command: (polar '(0 0 0) pi 1)
(-1.0 1.22465e-016 0.0)

....


Or, if in the case of such angles you can add or subtract a coordinate list instead....

The equivalent of the (polar) quoted above [for a point 1 unit to the left of 0,0,0] would be:

 

Command: (mapcar '- '(0 0 0) '(1 0 0))
(-1 0 0)

Kent Cooper, AIA
Message 6 of 14
BigDumbWeirdo
in reply to: dbroad

Is what I'm seeing right in that Acad uses double-precision? I thought it was single, since the Units dialog box maxes out at 8 digits after the decimal.

Message 7 of 14
dbroad
in reply to: BigDumbWeirdo

Yes double precision.  To print more, you could use (princ (rtos myvalue 2 14))

 

or something else like it.

Architect, Registered NC, VA, SC, & GA.
Message 8 of 14
BigDumbWeirdo
in reply to: dbroad

::grumble grumble:: Now I have to go re-write some older code...

 

But seriously, thanks to both you and Kent. Being the "AutoCAD Answer Guy" in my office, it's nice to know that there's still a place I can go to if I get stuck without revealing my ignorance to my co-workers. Smiley Very Happy

Message 9 of 14
stevor
in reply to: BigDumbWeirdo

These computers use binary math;

it is more efficient, especially for integer math.

If the numbers are 'converted'

to our more familiar decimal format,

more errors can accumulate in subsequent math,

because it averages out.

Or something like that.

 

If the numbers must be shown to the closest decimal:

 (setq p (polar '(0.0 0.0 0.0)  pi  1.0) x (car p) y (cadr p) z (caddr p))
 (defun N_E12 (N) (atof (rtos N 2 12)))
 ;(setq Xn (N_E12 X))
 ;(princ "\n X...16: ")(princ (rtos X 2 16))
 ;(princ "\n Xn..16: ")(princ (rtos Xn 2 16) )
 (setq Yn (N_E12 Y))
 (princ "\n Y....16: ")(princ (rtos y 2 16))
 (princ "\n Yn...16: ")(princ (rtos yn 2 16) )
 (princ "\n Prin1.Y: ") (prin1 y )
 (princ "\n Prin1.P: ") (prin1 p )
 (setq Pn (list (N_E12 X) (N_E12 Y) (N_E12 Z) ))
 (princ "\n Prin1.Pn: ") (prin1 pn )

(princ)

 

S
Message 10 of 14
Lee_Mac
in reply to: BigDumbWeirdo

In short, computer memory is finite and hence recurring & irrational values must be stored to a finite precision (approximately 15 decimal places for double precision floating point values). This memory limitation introduces minute rounding errors at the extent of the stored precision, and these minute errors accumulate over repeated calculation.

 

As noted by the other contributors to this thread, this behaviour is simply something that you need to become familiar with when programming: for example, when comparing doubles for equality, ensure that you use the AutoLISP equal function with some tolerance (I personally use 1e-8 (i.e. 0.00000001) since this is the limit of unit precision in AutoCAD and so values differing by a tolerance less than this would not have been introduced intentionally by the user when drafting).

 

For more information on the subject, this Wikipedia article may be interesting to read.

 

Lee

 

 

 

Message 11 of 14
_gile
in reply to: Lee_Mac

>> (approximately 15 decimal places for double precision floating point values)


Not 15 decimal places, but 15 significant digits, as shown the following examples

 

12345678901234500000 (0 decimal places)
1234567890.12345 (5 decimal places)

1.23456789012345 (14 decimal places)

0.000000000123456789012345 (25 decimal places)

 

 Therefore the accuracy is lost gradually as going away from the origin (as in some land survey drawings).


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 12 of 14
Lee_Mac
in reply to: _gile

Good catch gile - thanks.

Message 13 of 14
_gile
in reply to: Lee_Mac

I tried something to automatically compare numbers and points with a tolerance which takes in account the significant digits (not deeply tested).

 

(defun log10 (x)
  (if (zerop x)
    0.0
    (/ (log (abs x)) (log 10))
  )
)

(defun fuzz (x)
  (expt 10.0 (- (fix (log10 x)) 14))
)

(defun equalNumbers (x y)
(or (= x y) (equal x y (max (fuzz x) (fuzz y)))) ) (defun equalPoints (p1 p2) (vl-every 'equalNumbers p1 p2) )

(fuzz 0.0000123456789012345) =>1.0e-018
(fuzz 0.123456789012345) =>1.0e-014
(fuzz 12345.6789012345) =>1.0e-010
(fuzz 1234567890.12345) =>1.0e-005
(fuzz 123456789012345) =>1.0



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 14 of 14
_gile
in reply to: _gile

Here's a more mathematically rigorous implementation:

 

(defun gc:Log10 (x)
  (/ (log x) (log 10))
)

(defun gc:Fuzz (x)
  (if (zerop x)
    1e-15
    (expt 10.0 (fix (- (gc:Log10 (abs x)) 15)))
  )
)

(defun gc:EqualNumbers (x y)
  (or (= x y) (equal x y (max (gc:Fuzz x) (gc:Fuzz y))))
)

(defun gc:EqualPoints (p1 p2)
  (vl-every 'gc:EqualNumbers p1 p2)
)

 

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report

”Boost