Joel
Joel

Reputation: 1477

Unsigned comparison of numbers in Clojure?

I'm attempting to write an image processing library in Clojure, but I've run into a problem while writing my tests.

The image data is stored as a 2D array of integers, which are signed (Java, and by extension Clojure, doesn't have unsigned integers). I have a function to get a pixel at a given pair of coordinates. My test of that function looks something like this:

(is (= (get-pixel image 0 (dec width)) 0xFFFF0000))

That is, see if the pixel at (0, width-1) is red. The problem is, get-pixel returns a signed int, but Clojure treats 0xFFFF0000 as a long. In this case, get pixel is returning -65536 (0xFFFF0000 in hex), and Clojure is checking if it's equal to 4294901760.

Now, I have a few options. I could rewrite the test using the signed integer interpretation of the hex as a decimal (-65536), but I think that makes the intent of the test less clear. I could write a function to convert the negative number into a positive long, but that's an added layer of complexity. The simplest way is probably just to do a bitwise and between the two numbers and see if it's changed, but that still seems more complicated than it should be.

Is there any built-in way to force 0xFFFF0000 to be evaluated as a signed integer rather than a long, or to do a bitwise comparison of two arbitrary numbers? The int function doesn't work, as the number is too large to be represented as a signed int.

Thanks!

Upvotes: 4

Views: 1444

Answers (2)

amalloy
amalloy

Reputation: 91837

In clojure 1.3, there's an unchecked-int function, which just takes the bottom four bytes and makes them an int:

user> (unchecked-int 0xffff0000)
-65536

It's a bit sad that Clojure doesn't let you enter literal numbers of different sizes - here we're doing the equivalent of the Java (int)0xffff0000L.

Upvotes: 4

BillRobertson42
BillRobertson42

Reputation: 12883

What clojure version are you using? The primitive handling was changed quite a bit in version 1.3. I did a little bit of experimenting, and I seem to get different results than you describe.

user=> (int 0xFFFF0000)
IllegalArgumentException Value out of range for int: 4294901760  clojure.lang.RT.intCast (RT.java:1093)
user=> (long 0xFFFF0000)
4294901760
user=> *clojure-version*
{:major 1, :minor 3, :incremental 0, :qualifier nil}

If you're using 1.3 you could use the long function. You could also manipulate your data with a ByteBuffer and deal with the bytes a bit more directly, although you'll never get the level of control you would with C.

Upvotes: 0

Related Questions