Pull to refresh

Comments 27

И еще, много где встречал использование char как uint16
Для беззнакового short во многих ситуациях можно также использовать char.
Почему не упоминаете Integer(Long).compare(divide)Unsigned() методы?

Последние 4 осуществляют циклические сдвиги, их эквивалентов в Java нет.

А как же Integer.rotateLeft(Right)?
Оп-па, а я и забыл про rotateLeft-Right. Спасибо. А про Integer(Long).compare(divide)Unsigned() не понял — в стандартной библиотеке вроде бы такого нет.
В Java 8, которая уже, на минуточку, скоро полгода как основная версия.
У кого-то она, может, и основная, но самая распространённая Java-платформа недавно только на Java7 (и то не полностью) переползла.
Это здорово, что в 8 появились! А я смотрел в доках к семёрке, поэтому и не увидел. Но всё равно пока придётся уж иметь в виду ручные реализации деления и сравнения, поддержка семёрки всё-таки желательная вещь сейчас.
Посмотрите реализацию Long.divideUnsigned, он считает в BigInteger'ах если делитель отрицателен :(
Что-то туплю, в чём смысл «Integer.compare( a ^ 0x80000000, b ^ 0x80000000 );»?
Ведь мы меняем знак (старший бит) у обоих операндов, нет?
Эта операция (инвертирование знака) проецирует пространство значений [0;2^32-1] на [-2^31;2^31-1] для обоих операндов. Далее производится обычное сравнение. Например, было число -1 (0xffffffff), оно станет 0x7fffffff. Было число 10 (0x0000000a), станет -10 (0x8000000a). 0x7fffffff > 0x8000000a.
На самом деле это такая небольшая обфускация: правильнее будет Integer.compare(a - 0x80000000, b - 0x80000000);, где как бы понятно как всё работает. Мы переходим от чисел в интервале от 0 до 0xffffffff к часлам в интервале от 0x80000000 до 0x7ffffff путём линейного сдвига (мы стартуем с чисел без знака, а результатом является число без знака, но «так можно», потому что в арифметике «с дополнением до двух» сложение и вычитанием одинаково и для чисел без знака и для чисел со знаком) — а после этого уже все сравнения нормально можно производить.

Подобные же трюки часто приходится производить работая с SSE.

Ну а дальше можно заметить что 0x80000000 — это такое особенное число, что его что вычесть, что прибавить, что про XOR'ить — всё равно. Не знаю, правда, какой смысл в этой обфускации.
Не знаю, правда, какой смысл в этой обфускации.

Побитовые операции быстрее (хотя на дворе 2014 год, и может уже и не быстрее).
Сколько же вам лет-то? Побитовые операции были быстрее в компьютерах 70х годов (и то не во всех)! Уже в 80е сложение выполнялось с такой же скоростью как и побитовые операции!
Собственно, в 8 джаве так и сделано
public static int compareUnsigned(int x, int y) {
        return compare(x + MIN_VALUE, y + MIN_VALUE);
}
И зачем так сложно long делить.
Что плохо в этом:
    public static long ulongDiv(final long a, final long b) {
		return b>1L || b<0 ? (a>>>1)/(b>>>1) : a/b;
	}

Одна строчка — всё инлайнится и оптимизируется.
А теперь представьте, что через 3 года натыкаетесь на этот кусок кода. Сколько вам понадобиться времени, чтобы понять как это работает?
Если я его за пять секунд придумал, то очевидно, что пять секунд. А во-вторых, вся подобная магия должна быть документирована. Тут даже по имени функции видно, что она делает.
Это не более запутано чем Integer.compare( a ^ 0x80000000, b ^ 0x80000000 ) и уж точно гораздо более понятно, чем тот, кусок кода из пяти функций, который в статье приведен. Вы внимательно посмотрели, что именно в статье предлагается использовать? Четыре процедуры, два из которых весьма не очевидны. А уж оптимизация сравнивать…
И да, кстати, вы что не видите, что я просто беззнаково сокращаю делитель и делимое на два, выводя их в диапазон положительных чисел? По-моему, это заметно пятой секунде. Из которых четыре уходит на то, чтобы понять, что условие нужно для случая, когда делитель меньше двойки и сокращать нельзя.
ulongDiv(10, 3) возвращает 5
Косячок-с :) Нужно ещё одно условие ввести. :)
На самом деле нужно доказывать корректность для всего множества значений :-)
И желательно приложить сокращенный вариант доказательства прямо в JavaDoc.
Собственно, даже проще:
public static long ulongDiv(final long a, final long b) {
return b!=1L? (a>>>1)/(b>>>1): a/b;
}
В отличи от гугеля, понятно, инлайнится и скорее всего для констант будет просто статически вычислено. Причём, если b — константа, то тоже будет отлично заоптимизированно.
Да вы прямо математик, да. Делим 9 на 3 и получаем… 4. Это ж просто праздник какой-то!

P.S. Только не надо приводить ещё один «ещё более очевидный» вариант. В конце-концов доберётесь-таки до чего-то подобного Гугловому варианту. С 10й попытки. Я в вас верю. Но вопрос «почему всем идиотам всё далеко не так очевидно, как мне» вроде как уже отпал. Или нет?
Я бы еще добавил самой первой рекомендацией: Никогда не используйте нигде, кроме тщательно локализованных модулей взаимодействия с legacy данными и низкоуровневыми протоколами.
В библиотеке javolution есть класс Struct для работы с такого типа данными.
Для каких-то исключительных случаев такой метод, возможно, и будет полезным, но я в java скорее предпочту использовать знаковые типы достаточной для хранения данных разрядности, нежели городить подобные обёртки. Просто потому, что в процессе разработки все эти пляски забудутся, и начнутся чудеса при сравнении знаковых с беззнаковыми, «неправильными» операциями и т.д.
Если такая магия и нужна, то ее лучше обернуть классом, и использовать как объект-значение.
Жаль, что в Java (как и в большинстве языков) нельзя создавать производных типов над примитивными.
Sign up to leave a comment.

Articles