Как мне сравнить две длины как неподписанные в Java?
-
05-07-2019 - |
Вопрос
Я храню битовые шаблоны беззнаковых 64-битных чисел в long
переменная и хотите вычислить расстояние между двумя из них в диапазоне без знака.Потому что Java интерпретирует long
как целое число со знаком дополнения к двум, я не могу просто сделать a - b
, как показано в следующем примере:
// on the unsigned range, these numbers would be adjacent
long a = 0x7fffffffffffffffL;
long b = 0x8000000000000000L;
// but as two's complement (or any representation that
// stores the sign in the first bit), they aren't
assert b - a == 1;
Каков правильный способ сделать это?
Решение
Если вы имеете дело с сложением и вычитанием, не имеет значения, используете ли вы подписанный или неподписанный тип, если аргументы являются как подписанными, так и неподписанными. Если вам нужно сравнить a и b, сравните a-b с 0.
Другие советы
Когда арифметика оборачивается, она работает так же, как и в случае, который вы даете. Если вы интерпретируете результат как значение без знака, это будет верно для всех случаев - вы просто изменяете интерпретацию битового шаблона, он все еще установлен гомоморфно & # 918; 2 64 . р>
У меня работает:
long a = 0x7fffffffffffffffL;
long b = 0x8000000000000000L;
b - a = (long) 1
a - b = (long) -1
Я использовал это решение:
if (longA == longB) return 0;
return (longA < longB) ^ (longA < 0) ^ (longB< 0) ? 1 : -1;
Начиная с Java 8 , сравнение long
как целых чисел без знака можно выполнить с помощью Long.compareUnsigned (x, y) .
Вот простой бэкпорт для Java 7 и более ранних версий:
public static int compareUnsigned(long x, long y) {
return Long.compare(x + Long.MIN_VALUE, y + Long.MIN_VALUE);
}
Как упоминалось ранее, у вас не возникнет проблем с вычитанием, так что если это все, что вы пытаетесь сделать, то не волнуйтесь.
Но, согласно вашему примеру, сложение переполнится, и ни один из реляционных операторов не будет работать должным образом.Если это вызывает беспокойство, то вы можете написать свои собственные операции с отношениями или использовать лучший тип box, чем Long.
Решения:1.Используйте BigInteger вместо Long . Большой ИнтЕгратор был создан для выполнения вычислений с большими числами и может легко поддерживать 128-битные вычисления.
- Напишите свои собственные операции с отношениями и исключите использование сложения или умножения как возможности.Написать свой собственный реляционный оператор на самом деле не так уж и сложно.Сначала вы сравниваете наиболее значимый бит.Если старший бит одинаков для обоих чисел, вы можете замаскировать его, выполнив побитовое и (&) с помощью 0X7FFFFFFFFFFFFFFFF, а затем сравнить замаскированные значения.
Я использую следующий код:
static boolean unsignedLessThan(long left, long right) {
return (left < right) ^ (left < 0) ^ (right < 0);
}
(на примере Тамутнефрета)
Очевидно, вам нужно разобраться с битами.
static boolean compare(long a, long b)
{
if(( a & (Long.MAX_VALUE + 1)) != 0)
return ( b & (Long.MAX_VALUE + 1) ) != 0
? (a < b) //same sign
: true; //a is greater b
else
return ( b & (Long.MAX_VALUE + 1) ) != 0
? false //b is greater a
: a < b; //same sign
}
Или вы можете сделать пополам,
public static long unsignedDiff(long a, long b) {
long mask = 0xFFFFFFFFL;
return (( ((a >> 32) & mask) - ((b >> 32) & mask) ) << 32) +
+ ((a & mask) - (b & mask));
}