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.