8

I'm trying to use BigDecimals in Clojure to model (if necessary) arbitrary precision numbers. I have a strange error when I try to instantiate a BigDecimal from an unscaled value and scale factor:

user=> 1.31M
1.31M (OK)
user=> (class 1.31M)
java.math.BigDecimal (OK)
user=> (.unscaledValue 1.31M)
131 (OK)
user=> (.scale 1.31M)
2 (OK)
user=> (.movePointLeft (BigDecimal. 131) 2)
1.31M (OK)
user=> (BigDecimal. (BigInteger. "131") 2)
1.31M
user=> (BigDecimal. 131N 2) (WRONG!!!)
IllegalArgumentException No matching ctor found for class java.math.BigDecimal  clojure.lang.Reflector.invokeConstructor (Reflector.java:183)
user=> (BigDecimal. (BigInteger. "131") 2)
1.31M

The problem here is that the clojure big integer IS NOT a java.math.BigInteger object. Even (bigint x) doesn't work:

user=> (doc bigint)
-------------------------
clojure.core/bigint
([x])
  Coerce to BigInt
nil

And by the way BigInteger constructor doesn't directly accept numeric values. I know I could also do something like:

user=> (BigDecimal. (BigInteger. (.toByteArray (.unscaledValue 1.31M))) (.scale 1.31M))
1.31M

My question is: is there a more idiomatic way of directly manage BigInteger objects from Clojure? Or am I stuck to wrap everything in a custom library, like:

user=> (defn my-bigint [x] (BigInteger. (.toString x)))
#'user/my-bigint
user=> (my-bigint 131)
131
user=> (BigDecimal. (my-bigint 131) 2)
1.31M

Thanks in advance for the help!

UPDATE: I NEED a BigInteger for serialization purposes: my idea is to store a BigDecimal as a byte array and an integer. My problem is that in Clojure, if I want, I cannot pass the result of .unscaledValue back and forth 'cause Clojure doesn't handle BigInteger created from Integers (neither do Java, for what it matters):

user=> (BigInteger. 3)
IllegalArgumentException No matching ctor found for class java.math.BigInteger  clojure.lang.Reflector.invokeConstructor (Reflector.java:183)

A call to .toString on the number is unuseful for the semantics of serialization (and more error prone). I would like to know if in Clojure there is an idiomatic way to write something like:

user=> (bigdec 131N 2)

No .movePointLeft (creation of two different objects without benefits), no .toString (I have a number, I stringify it and then create a BigInteger, another number, from it?), no slow and indirect method: just plain BigInteger and scale value.

Vinz

4
  • wrap a function with bigdec source. Commented Jun 11, 2012 at 14:11
  • Indeed my thought at the moment was to submit a patch that slighty modifies the clojure source to accept scale factor (which is the main problem). If you put this (the scale factor problem and the source code to be modified) I'll accept your answer :) (BTW, I'm starting to think this is a limitation of Java API, but it's another topic...) Commented Jun 11, 2012 at 14:16
  • Accepted anyway, in the end your idea of bypassing BigInteger is not that bad, I'll wrap my head around it! Commented Jun 11, 2012 at 14:53
  • Is there a reason you're not using the biginteger function? Commented Jun 11, 2012 at 23:10

3 Answers 3

9
=> (type (.unscaledValue 1.31M))
java.math.BigInteger

=> (type (biginteger 131))
java.math.BigInteger

=> (BigDecimal. (biginteger 131) 2)
1.31M
Sign up to request clarification or add additional context in comments.

1 Comment

Indeed, and it's the right answer. I'm moving the 'accepted answer flag'.
4
user=> (.movePointLeft (bigdec 131) 2)
1.31M
user=> (.movePointLeft (bigdec 131N) 2)
1.31M

user=> (source bigdec)
(defn bigdec
  "Coerce to BigDecimal"
  {:tag BigDecimal
   :added "1.0"
   :static true}
  [x] (cond
       (decimal? x) x
       (float? x) (. BigDecimal valueOf (double x))
       (ratio? x) (/ (BigDecimal. (.numerator x)) (.denominator x))
       (instance? BigInteger x) (BigDecimal. ^BigInteger x)
       (number? x) (BigDecimal/valueOf (long x))
       :else (BigDecimal. x)))

1 Comment

Unfortunately, I have a requirement to store in byte format the number in an effective way, so I need the single bytes for serialization. With a BigInteger.toByteArray and a integer for scale I'm right, with a 131N to be serialized in a string I'm not!
0

I like this a bit better:

(-> 131M (.movePointLeft 2))

1 Comment

BTW, I love thrush operator ;)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.