2012-06-11 15 views
7

Sto cercando di utilizzare BigDecimals in Clojure per modellare (se necessario) numeri di precisione arbitrari. Ho un errore di strano quando si tenta di creare un'istanza di un BigDecimal da un fattore di scala di valore e non in scala:Clojure BigInt non è 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 

Il problema qui è che il grande numero intero clojure non è un oggetto java.math.BigInteger. Anche (bigint x) non funziona:

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

E a proposito BigInteger costruttore non accetta direttamente i valori numerici. So che potrei anche fare qualcosa di simile:

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

La mia domanda è: esiste un modo più idiomatico di gestire direttamente gli oggetti BigInteger da Clojure? O sono bloccato per avvolgere tutto in una libreria personalizzata, come:

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

Grazie in anticipo per l'aiuto!

UPDATE: Ho BISOGNO un BigInteger a fini di serializzazione: la mia idea è quella di memorizzare un BigDecimal come un array di byte e un intero. Il mio problema è che in Clojure, se voglio, non posso passare il risultato di .unscaledValue avanti e indietro perche' Clojure non gestisce BigInteger creato da numeri interi (né fare Java, per quello che conta):

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

Una chiamata a .toString sul numero non è utile per la semantica della serializzazione (e più incline agli errori). Vorrei sapere se in Clojure c'è un modo idiomatico per scrivere qualcosa di simile:

user=> (bigdec 131N 2) 

No .movePointLeft (creazione di due oggetti diversi, senza benefici), non .toString (Ho un certo numero, mi stringa i essa e quindi creare un BigInteger, un altro numero, da esso?), nessun metodo lento e indiretto: semplicemente BigInteger e valore di scala.

Vinz

+0

avvolgere una funzione con la fonte bigdec. –

+0

In effetti il ​​mio pensiero al momento era di presentare una patch che modifica leggermente la fonte del clojure per accettare il fattore di scala (che è il problema principale). Se metti questo (il problema del fattore di scala e il codice sorgente da modificare) accetterò la tua risposta :) (A proposito, sto iniziando a pensare che questa è una limitazione dell'API Java, ma è un altro argomento ...) –

+0

Accettato comunque, alla fine la tua idea di bypassare BigInteger non è poi così male, mi abbraccerò! –

risposta

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

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

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

questo è meglio. –

+0

Infatti, ed è la risposta giusta. Sto spostando la "risposta accettata". –

+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

Purtroppo, ho il requisito di memorizzare in byte il numero in modo efficace, quindi ho bisogno dei singoli byte per la serializzazione. Con un BigInteger.toByteArray e un intero per la scala, ho ragione, con un 131N da serializzare in una stringa non lo sono! –

0

Mi piace questo un po 'meglio:

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

Sto chiarendo la domanda, perché il requisito non sembra essere compreso ... :) –

+0

BTW, io amo l'operatore di moscerino;) –