In January Mark Engelberg brought up the subject of certain "missing"
math functions and provided some nice code to fix the situation. [1]
I'm still pretty new to Clojure, and I don't understand all of the
ins and outs of Mark's code, but I've come up with implementations of
a couple of functions (namely "floor" and "ceiling") that seem to do
the right thing but don't jump through all of the hoops as his
multimethods do. I believe that as long as "quot" and "rem" do the
right thing then my code does too. By "the right thing" here, I mean
basically the result type is what "quot" does:
user=> (class (quot 8.2 3.4))
java.lang.Integer
user=> (class (quot 8.2M 3.4))
java.lang.Integer
user=> (class (quot 8.2M 2))
java.math.BigDecimal
user=> (class (quot (bigint 83) (bigint 2)))
java.math.BigInteger
user=> (class (quot (bigint 83) 2))
java.math.BigInteger
user=> (class (quot 83 2))
java.lang.Integer
I guess it's trying to preserve the "exactness" of its input, so
BigDecimal/Integer => BigDecimal but BigDecimal/Double is already
"tainted" by inexactiude, and an Integer output is sufficient.
I have based my versions on the behavior of FLOOR and CEILING in
Common Lisp. In particular, each function takes a number and an
optional divisor (1 by default). Unfortunately, since Clojure does
not support multiple return values I have to call both "quot" and
"rem" rather than capturing both from a single call to TRUNCATE as in
CL.
(defn floor
([p] (floor p 1))
([p d] (let [q (quot p d)
r (rem p d)]
(if (neg? (* r d))
(- q 1)
q))))
(defn ceiling
([p] (ceiling p 1))
([p d] (let [q (quot p d)
r (rem p d)]
(if (pos? (* r d))
(+ q 1)
q))))
user=> (class (floor 8.2M 3.4))
java.lang.Integer
user=> (class (floor 8.2M 2))
java.math.BigDecimal
user=> (class (floor (bigint 83) (bigint 2)))
java.math.BigInteger
By contrast with Common Lisp, these functions behave more like FFLOOR
and FCEILING when given a BigDecimal arg, in other words, the return
value is an integer value but not an integer type. However, this is
how "quot" differs from TRUNCATE as well.
Am I missing something (likely), or is this approach feasible?
On a related note, shouldn't "quot" and "rem" also take an optional
divisor? It seems tiresome to do (quot 8.2 1). An alternative would
be to use "int" to truncate a number, but in certain cases "bigint"
would be appropriate. A single-argument "quot" should be able to
return the right type.
Finally, Mark discussed the difference in behavior between Java's
Math.round (0.5 rounded up) and CL's ROUND (0.5 rounded to nearest
even int). Is this an issue? Should Clojure simply follow Java here?
Aloha,
David Sletten
[1] http://groups.google.com/group/clojure/browse_frm/thread/
416aafd8e1858c77
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Clojure" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---