2012-06-11 10 views
7

Estoy tratando de usar BigDecimals en Clojure para modelar (si es necesario) números de precisión arbitrarios. Tengo un extraño error al intentar crear una instancia de un BigDecimal partir de un valor y factor de escala sin escala:Clojure BigInt no es Java BigInteger

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 

El problema aquí es que el clojure entero grande no es un objeto java.math.BigInteger. Even (bigint x) no funciona:

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

Y, por cierto, el constructor de BigInteger no acepta valores numéricos directamente. Sé que también podría hacer algo como:

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

Mi pregunta es: ¿hay una manera más directa idiomática de administrar objetos BigInteger de Clojure? O estoy atrapado para envolver todo en una biblioteca personalizada, como:

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

Gracias de antemano por la ayuda!

ACTUALIZACIÓN: I NECESIDAD un BigInteger para fines de serialización: mi idea es almacenar un BigDecimal como un conjunto de bytes y un número entero. Mi problema es que en Clojure, si quiero, no puedo pasar el resultado de .unscaledValue de ida y vuelta porque Clojure no maneja BigInteger creado a partir de los números enteros (tampoco lo hacen en Java, por lo que importa):

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

Una llamada a .toString en el número no es útil para la semántica de la serialización (y es más propensa a errores). Me gustaría saber si en Clojure hay una forma idiomática para escribir algo como:

user=> (bigdec 131N 2) 

Sin .movePointLeft (creación de dos objetos diferentes, sin beneficios), sin .toString (no tengo un número, que se stringify y luego crear un BigInteger, otro número, ¿no?), ningún método lento e indirecto: simplemente BigInteger y valor de escala.

Vinz

+0

envolver una función con fuente bigdec. –

+0

De hecho mi pensamiento en este momento era enviar un parche que modifica ligeramente la fuente de clojura para aceptar el factor de escala (que es el problema principal). Si pones esto (el problema del factor de escala y el código fuente a modificar), aceptaré tu respuesta :) (Por cierto, estoy empezando a pensar que esto es una limitación de la API de Java, pero es otro tema ...) –

+0

¡Aceptado de todos modos, al final tu idea de pasar por alto a BigInteger no es tan mala, la voy a entender! –

Respuesta

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

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

=> (BigDecimal. (biginteger 131) 2) 
1.31M 
+0

esto es mejor. –

+0

De hecho, y es la respuesta correcta. Moveré la 'bandera de respuesta aceptada'. –

+0

https://clojuredocs.org/clojure.core/biginteger – tar

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))) 
+0

Lamentablemente, tengo el requisito de almacenar en formato de bytes el número de una manera efectiva, por lo que necesito los bytes individuales para la serialización. Con un BigInteger.toByteArray y un entero para la escala, estoy en lo cierto, con un 131N que se serializará en una cadena que no soy. –

0

Me gusta este un poco mejor:

(-> 131M (.movePointLeft 2)) 
+0

Estoy aclarando la pregunta, porque el requisito no parece ser entendido ... :) –

+0

Por cierto, me encanta el operador de tordos;) –