2

I'm working on code that is hardware and and math related and makes extensive use of unsigned integer values, 32- and 64-bit. Bugs have been steady and difficult to work through without any javac support. Java 8 added a few functions on the boxed Long class for division, mod, and comparisons, but while these provide run-time support, the lack of compile-time warnings for unintended mixing of the two has made me want to pull my hair out.

Does anybody know of any ways to help deal with these issues? One of the Java team members mentions possible annotation support for type checking.

So far, I prefix all unsigned variables with u_, and I even tried adding an /* unsigned */ comment before each occurance of int or long that should be unsigned. While these were useful, they were also extremely cluttering. It doesn't go far enough. There are just too many opportunities for mistakes.

Two of the larger problems are unwanted sign extension and mixed operations.

Something as innocuous as /* unsigned */ long u_lowmask = 0xffffffff does not have the desired result or even just /* unsigned */ long u_widen64 = small32 ruins things with that silent extension. In other languages I can get warnings from the compiler or Lint-like static checker. Functional languages often build it into the type checking mechanism, but Java has eschewed these in favor of other solutions. There is just so many chances for a mixed comparison or operations.

Any ideas should hopefully have no runtime impact, so I can't afford wrapping unsigned in a class or bignums. Well, I might be able to wrap them in a class if I could get away with doing it without any allocation or the expense of thread locals, but this would probably make the code non-reentrant. Also Hotspot would have to be able to emit code for the wrapped unsigned integers close to simple native. (Actually, I think Hotspot is probably smart enough to get close - maybe an extra memory read and write.)

How are other people coding around this issue?

JasonN
  • 1,339
  • 1
  • 15
  • 27

2 Answers2

3

EDIT: As of July 2016, the Checker Framework ships with much or all of what you are asking for: the Signedness Checker, which verifies consistent use of signed vs. unsigned values. From the manual:

The Signedness Checker guarantees that signed and unsigned values are not mixed together in a computation. In addition, it prohibits meaningless operations, such as division on an unsigned value.

(Hat tip to @MAGx2 for noticing that this answer was out of date.)


Older answer follows:

I suggest that you look into the Checker Framework. It enables you to define your own type qualifier annotations such as @Unsigned, and then to check at compile time that your code is type-correct with respect to these annotations. If it issues no warnings, then you have a guarantee that your code doesn't mix signed and unsigned values.

The Checker Framework ships with 20 type-checkers, but not one for unsigned arithmetic. You would need to write your own type-checker. This should be relatively straightforward since the type-checker would need few special rules: you just don't want to mix signed and unsigned values.

For a list of JDK methods that need library annotations, see http://hg.openjdk.java.net/jdk8/tl/jdk/rev/71200c517524

mernst
  • 7,437
  • 30
  • 45
  • This is probably as close as I'm going to get. I'll look into it. This is also probably a great example of why some rules here on SO are poorly written and followed. It took me four rewrites of this question -- all previous ones being closed as asking for recommendations of a library or tool -- to finally get an answer which is a library or tool. What's in one dev's languages is an another dev's library. – JasonN Feb 02 '15 at 14:47
  • I found that there are checks for signed/unsigned in Checker Framework https://checkerframework.org/manual/#signedness-checker – MAGx2 Aug 16 '17 at 11:40
0

On the detection end, I haven't researched these much, but I would check out Lint4j or other Java lint tools from this answer.

Ideally the tool would give you a warning for every assignment of a long variable to an int value, then you could use a solution such as the following to wrap each such assignment in a static (inlineable) method call (thereby removing the warning).

For the assignment static wrapper methods, a utility class like this worked well for me in the past:

public class Convert {
    private Convert() {} // static utility methods only

    public long uintToUlong(int uint) {
        long ulong = 0;
        if (uint < 0) {
            uint &= Integer.MAX_VALUE;
            ulong = Integer.MAX_VALUE;
            ulong++;
        }
        return ulong + uint;
    }

    public long uintToLong(int uint) {
        return uintToUlong(uint)
    }

    public long intToUlong(int signedInt) {
        return signedInt; //TODO would this be correct?
    }

    public long intToLong(int signedInt) {
        return signedInt;
    }
}

Or perhaps this style would be more readable in your code:

public class Uint {
    /** this class has only static utility methods and is not to be instantiated */
    private Uint() {
        super();
    }

    public static long toUlong(int uint) {
        return Convert.uintToUlong(uint);
    }
}

Some JUnit tests:

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class UintTest {
    @Test
    public void testToUlong() {
        long expected = Double.valueOf(Math.pow(2, 32) - 1).longValue();
        assertEquals("maxUint", expected, Uint.toUlong(-1));
        expected = Double.valueOf(Math.pow(2, 31)).longValue();
        assertEquals("minInt", expected, Uint.toUlong(Integer.MIN_VALUE));
        expected--;
        assertEquals("maxInt", expected, Uint.toUlong(Integer.MAX_VALUE));
        expected = 10000;
        assertEquals("10000", 10000l, Uint.toUlong(10000));
        expected = 3000000000l;
        assertEquals("3B", expected, Uint.toUlong(1500000000 + 1500000000));
    }
}
Community
  • 1
  • 1
gknicker
  • 5,509
  • 2
  • 25
  • 41
  • For conversion, I just use `long ulong = 0xFFFFFFFFL & (long)uint`. Horribly enough, I accidentally left the `L` off the first time I wrote this. I immediately pulled this into a static function to prevent that again. – JasonN Jan 19 '15 at 21:48
  • 2
    @JasonN: That `static` function [already exists](http://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#toUnsignedLong-int-) – Holger Jan 20 '15 at 09:23