Author Topic: Function to return number of decimal places in real number  (Read 6927 times)

0 Members and 1 Guest are viewing this topic.

Ben Clark

  • Newt
  • Posts: 94
Function to return number of decimal places in real number
« on: January 18, 2018, 04:21:36 PM »
I could have sworn I saw this somewhere before. But I've just searched for a while and cannot find it.

Is there function that when given a real number, returns the number of decimal places the real number has?

Lee Mac

  • Seagull
  • Posts: 12906
  • London, England
Re: Function to return number of decimal places in real number
« Reply #1 on: January 18, 2018, 04:51:53 PM »
I think you'll find it difficult to obtain an answer other than 15 for most numerical arguments, given the rounding of doubles.

For a sensible answer, the function would require a string argument - given such, the task is reduced to counting characters following the decimal separator, after right-trimming zeros.

Ben Clark

  • Newt
  • Posts: 94
Re: Function to return number of decimal places in real number
« Reply #2 on: January 18, 2018, 06:17:39 PM »
I think you'll find it difficult to obtain an answer other than 15 for most numerical arguments, given the rounding of doubles.

For a sensible answer, the function would require a string argument - given such, the task is reduced to counting characters following the decimal separator, after right-trimming zeros.

hmm I see. Not being a programmer I guess I don't know how reals are stored.

Are they all the same amount of data, but the trailing zeros are truncated?

If I say (setq a 3.2), does a have 15 decimal places that are simply not shown?

Lee Mac

  • Seagull
  • Posts: 12906
  • London, England
Re: Function to return number of decimal places in real number
« Reply #3 on: January 18, 2018, 06:35:40 PM »
Are they all the same amount of data, but the trailing zeros are truncated?

If I say (setq a 3.2), does a have 15 decimal places that are simply not shown?

The AutoLISP Real data type is stored in double-precision floating-point format, typically known as a double; each value occupying the same amount of memory (64-bits). The issue to which I was referring to in my earlier post acknowledges the fact that when performing arithmetic operations with doubles, rounding errors will be introduced at the limit of the precision, therefore, when attempting to ascertain the precision of a Real, you will typically obtain the maximum precision of a double - usually 15 decimal places significant figures.

Nevertheless, for the fun of it, here's an alternative method to that which I described above, using DIMZIN manipulation:
Code: [Select]
;; < Incorrect code >


















(defun dp ( x / a b d )
    (setq d (getvar 'dimzin))
    (setvar 'dimzin 0)
    (setq a (rtos x 2 15))
    (setvar 'dimzin 8)
    (setq b (rtos x 2 15))
    (setvar 'dimzin d)
    (- 15 (- (strlen a) (strlen b)))
)

Or, using my earlier suggested method:
Code: [Select]
(defun dp ( x )
    (   (lambda ( s ) (- (strlen s) (vl-string-position 46 s nil t) 1))
        (vl-string-right-trim "0" (rtos x 2 15))
    )
)
« Last Edit: January 18, 2018, 06:55:36 PM by Lee Mac »

VovKa

  • Water Moccasin
  • Posts: 1626
  • Ukraine
Re: Function to return number of decimal places in real number
« Reply #4 on: January 18, 2018, 06:46:52 PM »
for the first dp
Code: [Select]
(dp 1234.56)

Lee Mac

  • Seagull
  • Posts: 12906
  • London, England
Re: Function to return number of decimal places in real number
« Reply #5 on: January 18, 2018, 06:53:02 PM »
for the first dp
Code: [Select]
(dp 1234.56)

Thanks VovKa - caught out by my own explanation even - 15 significant figures, not decimal places!  :oops:

Grrr1337

  • Swamp Rat
  • Posts: 812
Re: Function to return number of decimal places in real number
« Reply #6 on: January 18, 2018, 06:56:46 PM »
Code: [Select]
; _$ (_dp 4.12345 2) >> 5
; _$ (_dp 4.1234512345 2) >> 10
; _$ (_dp 4.1230001 2) >> 3 ; two zeros in a row allowed, when the last integer is 1
; _$ (_dp 4.1230001 6) >> 7 ; 6 zeros in a row allowed, when the last integer is 1
; _$ (_dp 4.2 6) >> 1 ; VovKa's Challenge
(defun _dp ( x 0fuzz )
  (if (eq 'REAL (type x))
    (
      (lambda ( L / i tmp )
        (cond
          (
            (and (= "1" (car L))
              (vl-every '(lambda (a) (= "0" (nth a L)))
                (cond
                  (0fuzz (repeat (setq 0fuzz (fix 0fuzz)) (setq tmp (cons (1+ (setq 0fuzz (1- 0fuzz))) tmp))))
                  ; ('(1 2 3)) ; default will be 3 zeros in a row if you uncomment this row (rhyme)
                ); cond
              ); vl-every
            ); and
            (setq tmp (cdr L)) (setq i -1)
            (while (= "0" (car tmp)) (setq i (1- i)) (setq tmp (cdr tmp)))
            (+ i (length L))
          )
          ( (length L) )
        ); cond
      ); lambda
      (mapcar 'chr
        (reverse
          (cdr
            (member 46
              (vl-string->list
                ; (vl-prin1-to-string x) ; THIS FAILS
                (rtos x 2 15)
              )
            )
          )
        )
      )
    )
  )
); defun _dp
« Last Edit: January 18, 2018, 07:42:59 PM by Grrr1337 »
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)
vevo.bg

VovKa

  • Water Moccasin
  • Posts: 1626
  • Ukraine
Re: Function to return number of decimal places in real number
« Reply #7 on: January 18, 2018, 06:57:49 PM »
Thanks VovKa - caught by my own explanation even - 15 significant figures, not decimal places!  :oops:
Lee, as soon as you are not sleeping there's a little surprise for you from autocad :)
Code: [Select]
(dp 4.2)

Grrr1337

  • Swamp Rat
  • Posts: 812
Re: Function to return number of decimal places in real number
« Reply #8 on: January 18, 2018, 07:46:53 PM »
Lee, as soon as you are not sleeping there's a little surprise for you from autocad :)
Code: [Select]
(dp 4.2)

Vovka, how that number became so evil?  :evil:
I've modified my suggestion, so it accounts fuzz of trailing zeros, when the very last integer is 1, just for cases like this:
Code: [Select]
_$ (rtos 4.2 2 15)
"4.200000000000001"
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)
vevo.bg

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Function to return number of decimal places in real number
« Reply #9 on: January 19, 2018, 05:42:42 AM »
Not being a programmer I guess I don't know how reals are stored.
Sort of. The way nearly all "real" numbers in computers (definitely including the way AutoCAD and therefore AutoLisp) are stored is similar to the Scientific format. I.e. a value scaled to 1 and include an exponent to tell you how far to move the decimal point up/down.

However all computers use a base 2 (binary) counting system instead of a base 10 (decimal). So the exponent works on base 2 instead of base 10. For a more in depth explanation, the "real" number used in AutoCAD (mostly) is called double-precision floating point (https://en.wikipedia.org/wiki/Double-precision_floating-point_format).

A further issue with this is that not all fractional numbers map perfectly between base 2 and base 10. E.g. a 1/5th in decimal is simply 0.2, but in binary it would be 0.0011001100... meaning it has an infinitely long repeating fractional part. Think similar to 1/3rd represented in decimal being 0.333333...

To check similar ideas: http://www.exploringbinary.com/binary-converter/

And then to actually see what happens inside the 64 bits of a double floating point number: https://www.h-schmidt.net/FloatConverter/IEEE754.html

Thus the sort of round-trip with some numbers tend to go awry ... as example using that 1/5th idea: Enter 0.2 as decimal, it gets converted to binary double floating point into 00111110010011001100110011001101 and when it gets displayed it gets converted back to decimal fixed point and you end up with: 0.20000000298023223876953125
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

Ben Clark

  • Newt
  • Posts: 94
Re: Function to return number of decimal places in real number
« Reply #10 on: January 19, 2018, 11:00:35 AM »
Not being a programmer I guess I don't know how reals are stored.
Sort of. The way nearly all "real" numbers in computers (definitely including the way AutoCAD and therefore AutoLisp) are stored is similar to the Scientific format. I.e. a value scaled to 1 and include an exponent to tell you how far to move the decimal point up/down.

However all computers use a base 2 (binary) counting system instead of a base 10 (decimal). So the exponent works on base 2 instead of base 10. For a more in depth explanation, the "real" number used in AutoCAD (mostly) is called double-precision floating point (https://en.wikipedia.org/wiki/Double-precision_floating-point_format).

A further issue with this is that not all fractional numbers map perfectly between base 2 and base 10. E.g. a 1/5th in decimal is simply 0.2, but in binary it would be 0.0011001100... meaning it has an infinitely long repeating fractional part. Think similar to 1/3rd represented in decimal being 0.333333...

To check similar ideas: http://www.exploringbinary.com/binary-converter/

And then to actually see what happens inside the 64 bits of a double floating point number: https://www.h-schmidt.net/FloatConverter/IEEE754.html

Thus the sort of round-trip with some numbers tend to go awry ... as example using that 1/5th idea: Enter 0.2 as decimal, it gets converted to binary double floating point into 00111110010011001100110011001101 and when it gets displayed it gets converted back to decimal fixed point and you end up with: 0.20000000298023223876953125


Thanks for those links, I feel like I fully understand floating point numbers now.


Are they all the same amount of data, but the trailing zeros are truncated?

If I say (setq a 3.2), does a have 15 decimal places that are simply not shown?

The AutoLISP Real data type is stored in double-precision floating-point format, typically known as a double; each value occupying the same amount of memory (64-bits). The issue to which I was referring to in my earlier post acknowledges the fact that when performing arithmetic operations with doubles, rounding errors will be introduced at the limit of the precision, therefore, when attempting to ascertain the precision of a Real, you will typically obtain the maximum precision of a double - usually 15 decimal places significant figures.

Nevertheless, for the fun of it, here's an alternative method to that which I described above, using DIMZIN manipulation:
Code: [Select]
;; < Incorrect code >


















(defun dp ( x / a b d )
    (setq d (getvar 'dimzin))
    (setvar 'dimzin 0)
    (setq a (rtos x 2 15))
    (setvar 'dimzin 8)
    (setq b (rtos x 2 15))
    (setvar 'dimzin d)
    (- 15 (- (strlen a) (strlen b)))
)

Or, using my earlier suggested method:
Code: [Select]
(defun dp ( x )
    (   (lambda ( s ) (- (strlen s) (vl-string-position 46 s nil t) 1))
        (vl-string-right-trim "0" (rtos x 2 15))
    )
)


Thanks for taking the time to write that! It seems like it works for all cases except the trailing 1 phenomenon.

VovKa

  • Water Moccasin
  • Posts: 1626
  • Ukraine
Re: Function to return number of decimal places in real number
« Reply #11 on: January 19, 2018, 11:23:53 AM »
It seems like it works for all cases except the trailing 1 phenomenon.
one more case
Code: [Select]
(setvar "DIMZIN" 8)
(dp 1.0)

Lee Mac

  • Seagull
  • Posts: 12906
  • London, England
Re: Function to return number of decimal places in real number
« Reply #12 on: January 19, 2018, 12:26:39 PM »
Thanks VovKa - caught by my own explanation even - 15 significant figures, not decimal places!  :oops:
Lee, as soon as you are not sleeping there's a little surprise for you from autocad :)
Code: [Select]
(dp 4.2)

Exactly my point with reply #1  :wink:

Lee Mac

  • Seagull
  • Posts: 12906
  • London, England
Re: Function to return number of decimal places in real number
« Reply #13 on: January 19, 2018, 12:31:19 PM »
It seems like it works for all cases except the trailing 1 phenomenon.
one more case
Code: [Select]
(setvar "DIMZIN" 8)
(dp 1.0)
Code: [Select]
(defun dp ( x )
    (   (lambda ( s ) (- (strlen s) (cond ((vl-string-position 46 s nil t)) ((1- (strlen s)))) 1))
        (vl-string-right-trim "0" (rtos x 2 15))
    )
)