2012-06-11 14 views
7

Próbuję użyć BigDecimals w Clojure do modelowania (jeśli to konieczne) dowolnych liczb precyzji. Mam dziwny błąd przy próbie instancji BigDecimal od współczynnika wartości i skalę nieskalowanego:Clojure BigInt nie jest Javą 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 

Problem polega na tym, że duża liczba całkowita clojure NIE jest obiektem java.math.BigInteger. Nawet (bigint x) nie działa:

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

A przy okazji konstruktor BigInteger nie przyjmuje bezpośrednio wartości liczbowych. Wiem, że mogę również zrobić coś takiego:

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

Moje pytanie brzmi: czy istnieje bardziej idiomatycznych sposób bezpośrednio zarządzać obiektami BigInteger z Clojure? Czy też utknąłem, aby zawinąć wszystko w niestandardowej bibliotece, na przykład:

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

Z góry dziękuję za pomoc!

AKTUALIZACJA: I POTRZEBUJ a BigInteger dla celów serializacji: moim pomysłem jest przechowywanie BigDecimal jako tablicy bajtów i liczby całkowitej. Moim problemem jest to, że w Clojure, jeśli chcę, mogę nie zdać wynik .unscaledValue iz powrotem bo Clojure nie obsługuje BigInteger utworzone z liczb całkowitych (ani zrobić Java, co ma znaczenie):

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

Wywołanie .toString na liczbie jest nieużyteczne dla semantyki serializacji (i bardziej podatne na błędy). Chciałbym wiedzieć, czy w Clojure istnieje idiomatycznych sposób napisać coś takiego:

user=> (bigdec 131N 2) 

Nie .movePointLeft (utworzenie dwóch różnych obiektów bez świadczeń), nie .ToString (mam numer, ja go stringify a następnie utworzyć BigInteger, inny numer, z niego?), bez powolnej i pośredniej metody: po prostu BigInteger i wartość skali.

Vinz

+0

zawinąć funkcję z bigdec źródła. –

+0

Rzeczywiście, w tej chwili w mojej głowie było przedstawienie łaty, która może zmodyfikować źródło czaru, aby zaakceptować współczynnik skali (co jest głównym problemem). Jeśli umieścisz to (problem ze skalą i kod źródłowy, który chcesz zmodyfikować) przyjmuję twoją odpowiedź :) (BTW, zaczynam myśleć, że jest to ograniczenie Java API, ale jest to inny temat ...) –

+0

Zaakceptowany mimo wszystko, w końcu twój pomysł obejścia BigInteger nie jest taki zły, otulę go głową! –

Odpowiedz

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

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

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

to jest lepsze. –

+0

Rzeczywiście, i to jest właściwa odpowiedź. Przenoszę flagę "zaakceptowanej odpowiedzi". –

+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

Niestety, mam wymóg przechowywania w formacie bajtowym liczby w efektywny sposób, więc potrzebuję pojedynczych bajtów do serializacji. Z BigInteger.toByteArray i liczbą całkowitą dla skali mam rację, z 131N do serializacji w ciągu znaków, nie jestem! –

0

Lubię to nieco lepiej:

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

Wyjaśniam pytanie, ponieważ wymóg nie wydaje się być zrozumiany ... :) –

+0

Przy okazji, kocham operatora pleśniawki;) –