`rational?` `decimal?` Am I misunderstanding these? (also `float?`)

175 views
Skip to first unread message

John Gabriele

unread,
Jun 11, 2015, 9:34:57 PM6/11/15
to clo...@googlegroups.com
My understanding is that a rational number is one that can be written as a fraction. For example, 5.1, which can be written as 51/10. But Clojure seems to disagree:

~~~
(rational? 51/10) ;=> true
(rational? 5.1)   ;=> false (?!)
~~~

Is my definition of "rational" incorrect?

Also, my understanding is that a decimal number is one that has a decimal point in it, like, for example, 5.1. However:

~~~
(decimal? 5.1) ;=> false (?!)
~~~

And while typing this, I also notice that while `integer?` acts like I'd expect, `float?` does something weird:

~~~
(integer? 5)   ;=> true     Yes
(integer? 5N)  ;=> true     Yes
(integer? 5.1) ;=> false   

(float? 5.1)  ;=> true
(float? 5.1M) ;=> false (?!)
~~~

Maybe I'm confusing "floating point number" with "decimal number" here? If so, what's the difference?

Thanks!

John Gabriele

unread,
Jun 11, 2015, 9:39:01 PM6/11/15
to clo...@googlegroups.com


On Thursday, June 11, 2015 at 9:34:57 PM UTC-4, John Gabriele wrote:

~~~
(integer? 5)   ;=> true     Yes
(integer? 5N)  ;=> true     Yes
(integer? 5.1) ;=> false   

(float? 5.1)  ;=> true
(float? 5.1M) ;=> false (?!)
~~~


Whoops. Please ignore the two "Yes"'s.

Also, I think I just fell further into the rabbit hole:

~~~
(rational? 5.1)  ;=> false
(rational? 5.1M) ;=> true (?!)

(decimal? 5.1)   ;=> false
(decimal? 5.1M)  ;=> true (?!)
~~~

Andy Fingerhut

unread,
Jun 12, 2015, 12:12:01 AM6/12/15
to clo...@googlegroups.com
Some related facts, but not sure if they offer a perfect justification for _why_ rational? and decimal? return what they do.

(source rational?) reveals that it returns true for integers, ratios, and decimal?, where (source decimal?) reveals it is true if it is of type BigDecimal.  Arithmetic operations like + - * / quot rem given integers and ratios should always return exact answers as other integers or ratios (barring division by 0).  I don't think that is true for BigDecimal's, though, at least with division.

Regarding rational numbers as ones that can be written as a fraction, all float and double values except NaN, and positive and negative infinity can be written exactly as fractions, too.  However, + - * / usually give only approximate answers, not exact.

Andy

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Fluid Dynamics

unread,
Jun 12, 2015, 1:32:11 AM6/12/15
to clo...@googlegroups.com
On Friday, June 12, 2015 at 12:12:01 AM UTC-4, Andy Fingerhut wrote:
Some related facts, but not sure if they offer a perfect justification for _why_ rational? and decimal? return what they do.

(source rational?) reveals that it returns true for integers, ratios, and decimal?, where (source decimal?) reveals it is true if it is of type BigDecimal.  Arithmetic operations like + - * / quot rem given integers and ratios should always return exact answers as other integers or ratios (barring division by 0).  I don't think that is true for BigDecimal's, though, at least with division.

Regarding rational numbers as ones that can be written as a fraction, all float and double values except NaN, and positive and negative infinity can be written exactly as fractions, too.  However, + - * / usually give only approximate answers, not exact.

Also, these floats and doubles always have a denominator that's a power of 2. Which means thirds, fifths, tenths, and the like generally won't be represented exactly (unlike with Ratio), though halves, quarters, and eighths can be. BigDecimal has a power-of-10 denominator, so it can additionally represent fifths and tenths exactly, but still not thirds (rounded, or non-repeating decimal exception if you haven't set a precision limit).

Most of the numeric types and their behavior are documented fully in Java's documentation -- the language documentation for floats and doubles, and the Javadocs for the java.math.BigDecimal class for BigDecimal. The clojure.lang.Ratio class is best understood as simply keeping track of a numerator/denominator pair of BigIntegers which can stay exact, but in some numerical uses will quickly gobble up memory using huger and huger denominators (and numerators, most likely) to do so. If the space (and slowdown!) becomes a problem you then need to decide on a precision restriction and use floats, doubles, or BigDecimals -- or "work smarter, not harder" by using a different algorithm to do whatever you are trying to do. (A perturbation theoretic approach might allow you to use a few high precision Ratios or BigDecimals and do most of the work with deltas from these that are maintained in ordinary doubles, for example. It depends on the numerics you're doing whether, and how, you can do this.)

Gary Verhaegen

unread,
Jun 12, 2015, 12:08:56 PM6/12/15
to clo...@googlegroups.com
Your definitions are correct, but in a different domain: you are using the mathematical definitions.

What these functions give you is information about the representation of the numbers in memory, not information about the numbers themselves. For example, (integer? 1.0) will give you false.

A rational number in mathematics is one that can be written as a fraction (of integers), or equivalently one that does not have an infinite and non-periodic expansion. A rational number in the sense of "rational?" in Clojure is an object in memory which is actually stored as a couple of integers, not merely a valie that could be stored that way.

As others have mentioned, the way in which a number is stored will have some impact on how much memory it takes up, how fast the computer can compute operations on it, and how much precision will be lost with these operations.

As always, Clojure puts more emphasis on behaviours and interfaces than on implementations, so you should really understand "rational?" as "will I get an exact answer if I use operations that would give an exact answer with a rational number (in the mathematical sense)?"

For example, (/ 1M 3) will throw an exception rather than returning an inexact, truncated answer, whereas (/ 1.0 3) will happily lie to you. (According to the documentation, BigDecimal always returns a correct value or throws an exception, except if you explicitly tell it to round. It can represent values down to about 1e-2_147_483_647, and up to filling your computer's memory.)

For a first step towards understanding floating-point values, I would recommend reading:

John Gabriele

unread,
Jun 15, 2015, 1:44:55 PM6/15/15
to clo...@googlegroups.com
Thanks for the clarifications, all. Had not thought about exact vs. inexact results.

Also, I see that --- if I simply want to know if a number is a (mathematical) decimal number, I can just do `(or (float? x) (decimal? x))`.

-- John

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscribe@googlegroups.com.

Andy Fingerhut

unread,
Jun 15, 2015, 2:15:43 PM6/15/15
to clo...@googlegroups.com
If your definition of a mathematical decimal number is 'one that when written in decimal has a decimal point in it', do you want the answer 'true' or 'false' for the floating point number 1.0?  I don't see how that definition of decimal number makes a lot of sense, or why you would want a function that tells you that.

If you want a function that tells you 'can this number be represented exactly as an integer?', then perhaps something like (== x (convert-to-nearest-BigInteger x)), but I am not sure off hand a good way to write convert-to-nearest-BigInteger.  According to one answer to this StackOverflow question, converting a double or float to BigDecimal first, then to the nearest BigInteger, may work: http://stackoverflow.com/questions/17960186/is-there-anyway-to-convert-from-double-to-biginteger

Andy


For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages