Java: числа без знака - PullRequest
       44

Java: числа без знака

4 голосов
/ 08 октября 2009

Есть ли в Java способ использовать числа без знака, как в (My) SQL?

Например: я хочу использовать 8-битную переменную (byte) с диапазоном вроде: 0 ... 256; вместо -128 ... 127.

Ответы [ 7 ]

9 голосов
/ 08 октября 2009

Нет, у Java нет никаких беззнаковых примитивных типов, кроме char (который имеет значения 0-65535, эффективно). Это боль (особенно для byte), но так оно и есть.

Обычно вы либо придерживаетесь того же размера и переполняетесь негативами для «старших» чисел, либо используете более широкий тип (например, short для byte) и справляетесь с дополнительными требованиями к памяти.

3 голосов
/ 08 октября 2009

Вы можете использовать класс для симуляции числа без знака. Например

public class UInt8 implements Comparable<UInt8>,Serializable
   {
   public static final short MAX_VALUE=255;
   public static final short MIN_VALUE=0;
   private short storage;//internal storage in a int 16

   public UInt8(short value)
      {
      if(value<MIN_VALUE || value>MAX_VALUE) throw new IllegalArgumentException();
      this.storage=value;
      }

   public byte toByte()
      {
      //play with the shift operator ! << 
      }
  //etc...
   }
2 голосов
/ 09 октября 2009

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

Однако, если вы используете устройство хранения меньшего размера, оно должно маскировать их или проверку диапазона или что-то для каждой операции.

когда-нибудь замечали, что char (любая операция) char дает int? Они просто не ожидают, что вы будете использовать эти другие типы.

Исключениями являются массивы (которые, я думаю, будут упакованы) и ввод-вывод, где вы можете использовать более мелкий тип ... но маскирование также будет работать.

2 голосов
/ 08 октября 2009

В основном вы можете использовать номера со знаком, как если бы они были без знака. Большинство операций остаются прежними, некоторые должны быть изменены. Смотрите этот пост .

1 голос
/ 08 октября 2009

Нет, вы не можете это изменить. Если вам нужно что-то большее, чем 127, выберите что-то большее, чем байт.

0 голосов
/ 05 января 2016

Я довольно новичок в Java и в программировании. Тем не менее, в последнее время я столкнулся с такой же ситуацией, когда потребовались значения без знака.

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

Общая идея состоит в том, чтобы создать интерфейс, я назвал его: UnsignedNumber<Base, Shifted> и расширить Number.class, реализуя абстрактный класс AbstractUnsigned<Base, Shifted, Impl extends AbstractUnsigned<Base, Shifted, Impl>>.

Таким образом, Базовый параметризованный тип представляет базовый тип, Shifted представляет фактический тип Java. Impl является ярлыком для реализации этого абстрактного класса.

Большую часть времени потребляет шаблон Java 8 Lambdas и внутренние частные классы и процедуры безопасности. Важным было добиться поведения без знака, когда математическая операция, такая как вычитание или сложение с отрицательным значением, порождает нулевой предел: переполняет верхний предел со знаком назад.

Наконец, потребовалось еще несколько дней, чтобы кодировать фабрики и подклассы реализации.

Пока я знаю: UByte и MUByte UShort и MUShort UInt и MUInt ... и т. Д.

Они являются потомками AbstractUnsigned: UByte или MUByte extension AbstractUnsigned<Byte, Short, UByte> или AbstractUnsigned<Byte, Short, MUByte> UShort или MUShort extension AbstractUnsigned<Short, Integer, UShort> или AbstractUnsigned<Short, Integer, MUShort> ... и т.д.

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

UPDATE: (Благодаря Ajeans добрым и вежливым указаниям)

/**
* Adds value to the current number and returns either
* new or this {@linkplain UnsignedNumber} instance based on
* {@linkplain #isImmutable()}
*
* @param value value to add to the current value
* @return new or same instance
* @see #isImmutable()
*/
public Impl plus(N value) {
    return updater(number.plus(convert(value)));
}

Это доступный извне метод AbstractUnsigned<N, Shifted, Impl> (или, как было сказано ранее AbstractUnsigned<Base, Shifted, Impl>); Теперь, чтобы работать под капотом:

private Impl updater(Shifted invalidated){
    if(mutable){
        number.setShifted(invalidated);
        return caster.apply(this);
    } else {
        return shiftedConstructor.apply(invalidated);
    }
}

В приведенном выше частном методе mutable - это private final boolean из AbstractUnsigned. number - это один из внутренних частных классов, который занимается преобразованием Base в Shifted и наоборот. Что имеет значение в соответствии с предыдущим «, что я делал прошлой летней частью » это два внутренних объекта: caster и shiftedConstructor:

final private Function<UnsignedNumber<N, Shifted>, Impl> caster;
final private Function<Shifted, Impl> shiftedConstructor;

Это параметризованные функции для приведения N (или Base) к Shifted или для создания нового экземпляра Impl, если текущий экземпляр реализации AbstractUnsigned<> является неизменным.

Shifted plus(Shifted value){
    return spawnBelowZero.apply(summing.apply(shifted, value));
}

В этом фрагменте показан метод добавления объекта number. Идея состояла в том, чтобы всегда использовать Shifted для внутреннего использования, потому что неясно, когда будут появляться положительные пределы «оригинального» типа. shifted является внутренним параметризованным полем, которое содержит значение целого AbstractUnsigned<>. Другие два Function<> производных объекта приведены ниже:

final private BinaryOperator<Shifted> summing;
final private UnaryOperator<Shifted> spawnBelowZero;

Первый выполняет сложение двух Shifted значений. И последний выполняет порождение ниже нуля транспонирования.

А теперь пример с одного из заводских шаблонов 'ад' для AbstractUnsigned<Byte, Short> специально для упомянутых ранее spawnBelowZero UnaryOperator<Shifted>:

...,
         v-> v >= 0
         ? v
         : (short) (Math.abs(Byte.MIN_VALUE) + Byte.MAX_VALUE + 2 + v),
...

, если Shifted v положительно, в действительности ничего не происходит, и возвращается исходное значение. В противном случае: необходимо вычислить верхний предел типа Base, равный Byte, и добавить к этому значению отрицательное значение v. Если, скажем, v == -8, то Math.abs(Byte.MIN_VALUE) будет выдавать 128, а Byte.MAX_VALUE будет выдавать 127, что дает 255 + 1, чтобы получить исходный верхний предел, который был вырезан битом знака, как получил, и так желательно 256 на месте. Но самое первое отрицательное значение - это , на самом деле это 256, поэтому снова +1 или +2 в целом. Наконец, 255 + 2 + v, что составляет -8, дает 255 + 2 + (-8) и 249

Или более наглядно:

0 1 2 3 ... 245 246 247 248 249 250 251 252 253 254 255 256
                             -8  -7  -6  -5  -4  -3  -2  -1

И чтобы завершить все это: это определенно не облегчит вашу работу или сэкономит байты памяти, но у вас будет довольно желательное поведение, когда это необходимо. И вы можете использовать это поведение в значительной степени с любыми другими Number.class подклассами. AbstractUnsigned будучи подклассом Number.class, сам предоставляет все удобные методы и константыаналогично другим «родным» подклассам Number.class, включая MIN_VALUE и MAX_VALUE и многим другим, например, я кодировал удобный метод для изменяемых подклассов, называемый makeDivisibileBy(Number n), который выполняет простейшую операцию value - (value % n).

Мое первоначальное стремление было показать, что даже нуб, такой как я, может его кодировать. Моим первоначальным стремлением, когда я программировал этот класс, было получить удобный универсальный инструмент для постоянного использования.

0 голосов
/ 04 июля 2013

Если вам нужно оптимизировать хранилище (например, большая матрица), вы можете кодировать большие положительные числа с отрицательными числами, чтобы сэкономить место. Затем вам нужно сдвинуть числовое значение, чтобы получить действительное значение при необходимости. Например, я хочу манипулировать только короткими положительными числами. Вот как это возможно в Java:

        short n = 32767;
        n = (short) (n + 10);
        System.out.println(n);     
        int m = (int) (n>=0?n:n+65536); 
        System.out.println(m);

Таким образом, когда короткое целое число превышает диапазон, оно становится отрицательным. Тем не менее, по крайней мере, вы можете сохранить это число в 16 битах и ​​восстановить его правильное значение, добавив значение сдвига (число различных значений, которые можно кодировать). Значение должно быть восстановлено в большем типе (int в нашем случае). Это может быть не очень удобно, но я считаю, что это так в моем случае.

...