2015-01-19 3 views
2

Я работаю над кодом, связанным с оборудованием и математикой, и широко использует беззнаковые целочисленные значения, 32- и 64-разрядные. Ошибки были устойчивыми и трудными для работы без поддержки javac. Java 8 добавила несколько функций в бокс-классе Long для деления, мод и сравнений, но пока они обеспечивают поддержку во время выполнения, отсутствие предупреждений о компиляции для непреднамеренного смешивания этих двух заставил меня хотеть вытащить мои волосы ,Идеи для решения целых чисел без знака в Java 8

Кто-нибудь знает какие-либо способы помочь решить эти проблемы? Один из членов команды Java упоминает possible annotation support для проверки типа.

До сих пор я префикс всех неподписанные переменных u_, и я даже пытался добавив /* unsigned */ комментарий перед каждым из int вхождения или long, которые должны быть подписаны. Хотя они были полезны, они также были чрезвычайно захламлены. Это не слишком далеко. Есть слишком много возможностей для ошибок.

Две из больших проблем - это нежелательное расширение знака и смешанные операции.

Нечто безобидное, как /* unsigned */ long u_lowmask = 0xffffffff, не имеет желаемого результата или даже просто /* unsigned */ long u_widen64 = small32 разрушает вещи с помощью этого бесшумного расширения. На других языках я могу получить предупреждения от компилятора или Lint-like static checker. Функциональные языки часто встраивают его в механизм проверки типов, но Java избегает их в пользу других решений. Есть очень много шансов на смешанное сравнение или операции.

Любые идеи, возможно, не имеют никакого воздействия во время работы, поэтому я не могу позволить себе обернуть без знака в классе или бонусах. Ну, я мог бы обернуть их в класс, если мне удастся это сделать без какого-либо выделения или затрат на локаторы потоков, но это, вероятно, сделает код невозвратным. Также Hotspot должен был бы иметь возможность испускать код для завернутых целых чисел без знака, близких к простым native. (На самом деле, я думаю, что Hotspot, вероятно, достаточно умный, чтобы приблизиться - возможно, дополнительная память читает и пишет.)

Как другие люди кодируют эту проблему?

+0

переписан, чтобы удалить ссылку на библиотеку или инструментов. – JasonN

+0

Я думаю, что решение, основанное на аннотации, было бы самым чистым, но afaik не будет подходящего инструмента проверки. – Holger

ответ

3

EDIT: По состоянию на июль 2016 года Checker Framework поставляется с большим количеством или всего того, что вы запрашиваете: Signedness Checker, который проверяет последовательное использование подписанных значений без знака.От manual:

Контроллер проверки подлинности гарантирует, что подписанные и неподписанные значения не будут смешиваться вместе при вычислении. Кроме того, он запрещает бессмысленные операции, такие как деление на значение без знака.

(Наконечник шляпы @ MAGx2 для заметив, что этот ответ был устареть.)


Пожилым ответ следующим образом:

Я полагаю, что вы смотрите в Checker Framework. Он позволяет определить ваши аннотации классификатора типа, такие как @Unsigned, а затем проверить во время компиляции, что ваш код является корректным по отношению к этим аннотациям. Если он не выдает никаких предупреждений, тогда у вас есть гарантия, что ваш код не будет смешивать подписанные и неподписанные значения.

Checker Framework поставляется с 20 type-checkers, но не для арифметики без знака. Вам нужно будет write your own type-checker. Это должно быть относительно простым, так как для проверки типов потребуется несколько специальных правил: вы просто не хотите смешивать подписанные и неподписанные значения.

Список методов JDK, которые нуждаются в библиотеку аннотаций см http://hg.openjdk.java.net/jdk8/tl/jdk/rev/71200c517524

+0

Это, вероятно, как как я собираюсь. Я посмотрю. Это также, вероятно, отличный пример того, почему некоторые правила здесь на SO плохо написаны и следуют. Мне потребовалось четыре переписывания этого вопроса - все предыдущие закрыты, чтобы просить рекомендации библиотеки или инструмента - чтобы наконец получить ответ, который является библиотекой или инструментом. То, что находится на одном языке разработчиков, - это библиотека другого разработчика. – JasonN

+0

Я обнаружил, что есть проверки для подписанных/неподписанных в Checker Framework https://checkerframework.org/manual/#signedness-checker – MAGx2

0

В конце обнаружения я не исследовал их, но я бы посмотрел Lint4j или другие инструменты Java lint от this answer.

В идеале инструмент даст вам предупреждение для каждого присвоения long переменных к int значению, то можно использовать раствор, такие как следующий, чтобы обернуть каждое такое назначение в статическом (inlineable) вызов метода (тем самым удаляя предупреждение).

Для назначения статических методов оберток, утилита класса, как это работает хорошо для меня в прошлом:

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; 
    } 
} 

Или, возможно, этот стиль был бы более удобным для чтения в вашем коде:

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); 
    } 
} 

Некоторые тесты JUnit:

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)); 
    } 
} 
+0

Для преобразования я просто использую 'long ulong = 0xFFFFFFFFL & (long) uint'. Как ни странно, я случайно покинул «L» с первого раза, когда я это написал. Я сразу же потянул его в статическую функцию, чтобы снова это предотвратить. – JasonN

+2

@JasonN: Эта функция 'static' [уже существует] (http://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#toUnsignedLong-int-) – Holger

Смежные вопросы