On Saturday, June 24, 2017 at 3:36:21 AM UTC-5, henrik42 wrote:
>
> Hi,
>
> I'm doing a little write-up on Java basics and comparing some of them
> to Clojure (things like mutable shared state, side effects and so
> on). When I came to "numbers" I was surprised by some of the things I
> found in Clojure.
>
>     (== (double 0.5) (float 0.5)) ;; -> true
>     (== (double 0.2) (float 0.2)) ;; -> false
>

Due to how single and double precision floats are stored, you'll get these 
same results in Java too. These will store differently imprecise 
representations of the number (remember base 2, not 10).

user=> (Integer/toBinaryString (int (Float/floatToRawIntBits (float 0.2))))
"111110010011001100110011001101"
user=> (Long/toBinaryString (Double/doubleToRawLongBits 0.2))
"11111111001001100110011001100110011001100110011001100110011010"

More:
http://steve.hollasch.net/cgindex/coding/ieeefloat.html
https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
 

> The docs (
> https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/==)
> say, that `==` compares the numbers type-independently. But why are
> the two __representations__ (types?) of the numerical value `0.2`
> different then?
>
> I understand that `(float 0.2)` gets _converted_ to `double` and this
> conversion is done like this (just the way Java does it -- should be
> `f2d` in byte code).
>
>     (double (float 0.2)) ;; -> 0.20000000298023224
>     
> So that's not equal to `(double 0.2)`. But why not convert `float` to
> `double` like this:
>
>     (defn to-double [f]
>         (Double. (str f)))
>
> Here we're not converting the (inexact) `float` approximation of `0.2`
> to `double` but we use what human readers perceive as the (exact)
> _numerical value_.
>

The reason for this is that Clojure doesn't support floats - we only read 
fixed precision floating point as doubles, period. How would the reader 
know whether to read single or double precision? It would have to make a 
decision on whether to make a float or a double, which is inherently 
sketchy. The use cases for single precision floats even in Java are almost 
entirely gone at this point. The only time you'd want to use them is if you 
want floating point math with the smallest possible memory (and are willing 
to give up the corresponding reduction in precision). 
 

> This would parallel the way `BigDecimal` literals work in Clojure:
>
>     0.3M ;; -> 0.3M
>     (BigDecimal. 0.3) ;; -> 
> 0.299999999999999988897769753748434595763683319091796875M
>     (BigDecimal. (str 0.3)) ;; -> 0.3M
>

BigInteger and BigDecimal support a string constructor because they are 
arbitrary precision - that's the only way you can represent arbitrarily 
large numbers. If you want arbitrary precision, use those, otherwise they 
are not applicable to the case of floats/doubles. Note that the Clojure 
reader can use the suffix (N or M) in Clojure to determine when to activate 
arbitrary precision reading in this case.
 

>
> When we use numbers in sets and maps, more questions come up:
>
>     (into #{} [0.5 (float 0.5)]) ;; -> #{0.5}
>     (into #{} [0.2 (float 0.2)]) ;; -> #{0.2 0.2}
>
>
This is under discussion in a ticket at 
http://dev.clojure.org/jira/browse/CLJ-1649. It is definitely a bug with 
float/double hash consistency. My personal vote in this case is that floats 
and doubles should just never compare equals, period. Due to the example up 
top, you're almost never going to get predictable results and you should 
just not use floats. Or at least not use a mix of floats and doubles.
 

> First it seems that `==` is used to check for equality, but I think
> it's not the case.
>
>     (= 0.5 (float 0.5)) ;; -> true
>
> Ahh -- seems that `(float 0.5)` gets converted to `double` before
> comparing via `=`.
>
> Getting `#{0.2 0.2}` is bad: we won't be able to read this set literal
> back in.
>
>     #{0.2 0.2} ;; -> java.lang.IllegalArgumentException: Duplicate key: 0.2
>
> So my question is: does anyone know about tutorials, docs etc. on
> clojure.org or elsewhere where I can find advices/best practice/a
> "clojure specifications" (like the JLS does it for Java) on this
> topic.
>

Andy Fingerhut has a comprehensive page on equality at 
https://github.com/jafingerhut/thalia/blob/master/doc/other-topics/equality.md. 
I have an extensive rewrite of this destined to be a guide on the Clojure 
site, but it's still a work in progress at this point.
 

>
> Henrik
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@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+unsubscr...@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+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to