decimal values are incorrectly treated as System.Int64

52 views
Skip to first unread message

cees van Kemenade

unread,
Jun 26, 2018, 1:17:23 PM6/26/18
to clojure-clr

The decimal type is not handled correctly in the Clojure-clr it seems. I've tested it in Clojure-clr version 1.7 and 1.8. The Clojure-java (when working with BigDecimals) worked fine. It seems like clojure is casting decimals to Int64, thus losing all values behind the decimal point to early. Below you find an example performed on the repl of the clojure-clr 1.8

The next statements are all returning the incorrect result.
user=> (> (decimal 1.1)  (decimal 1.0))
false                           ;; however expected true
user=> (<= (decimal 1.1)  1)
true                            ;; however expected false
user=> (< (decimal 1.0)  (decimal 1.1))
false                           ;; however expecting true
user=> (<= (decimal 1.0)  (decimal 1.1))
true                            ;;  Hey, this works
user=> (<= (decimal 1.9)  (decimal 1.1))
true                            ;;  Oh, no it does not work
user=> (<= (decimal 2.0)  (decimal 1.1))
false                           ;; yes it works again if the value before the decimal point moves to the next value.

So it seems decimals are treated as System.Int64
Below you find some more examples indicating in this direction.



Creating a range of doubles and decimals for testing
user=> (def dbls '(1.0 1.1 1.2 2.0 2.1 2.2))
#'user/dbls
user=> (def decis (map decimal dbls))
#'user/decis
user=> decis
(1 1.1 1.2 2 2.1 2.2)
user=> (map type decis)
(System.Decimal System.Decimal System.Decimal System.Decimal System.Decimal System.Decimal)

That all seems to be oke.
Now let us map the minus operator over it, and it seems that all values are truncated to the nearest lower int

user=> (map - decis)
(-1 -1 -1 -2 -2 -2)

Of course mapping a - operator over it to negate a value might look a bit weird,  so let us use a multiplication.
user=> (map #(* -1.0 %) decis)
(-1.0 -1.1 -1.2 -2.0 -2.1 -2.2)

That is much better, but what happens if we multiply by a decimal?

user=> (map #(* (decimal -1.0) %) decis)
(-1 -1 -1 -2 -2 -2)

Now we have the truncation example again. Let us see what the type reveals.

user=> (map type (map #(* (decimal -1.0) %) decis))
(System.Int64 System.Int64 System.Int64 System.Int64 System.Int64 System.Int64)
user=>

Apparantly results are of type System.Int64 when using two decimals as input.

Let us see what happens with comparison operators: when comparing subsequent values:

user=> (map <  decis (rest decis))
(false false true false false)

That is clearly incorrect as we started of with an strictly increasing sequence of values
However, when I use doubles as one of the parameters I get the correct result:

user=> (map <  decis (rest dbls))
(true true true true true)

The > operator seems to be working fine at first sight:

user=> (map > decis (rest decis))
(false false false false false)

However, that is a coincidence as the example below shows
user=> (> (decimal 1.1)  (decimal 1.0))
false

Again it seems to be that only the values in front of the decimal point re use
user=> (<= (decimal 1.1)  1)
true

Which is incorrect. 
However, when doing the comparison against a double things are fine again.
user=> (<= (decimal 1.1)  1.0)
false
user=> (<= (decimal 1.1)  1.5)
true

I think this unexpected behavior can lead to unpleasant surprises.
So please stick to doubles.
Should I post this to Clojure-clr Jira?

regards,
Cees.

dmiller2718

unread,
Jul 18, 2018, 8:52:39 AM7/18/18
to clojure-clr
the decimal type is handled inconsistently.
the problem goes back to the early days of the port.
I'll track this one down.

dmiller2718

unread,
Jan 30, 2019, 5:22:22 PM1/30/19
to clojure-clr
Reply all
Reply to author
Forward
0 new messages