Ответ 1
Хорошо, самое короткое решение без условного значения, вероятно:
return (i|-i) >>> 31;
Я хочу реализовать f(int x) { return x == 0 ? 0 : 1; }
в Java.
В C я просто "return !!x;
", но !
не работает так, как в Java. Есть ли способ сделать это без условностей? Без чего-то дрянного, как развернутая версия
int ret = 0;
for (int i = 0; i < 32; i++) {
ret |= ((x & (1 << i)) >>> i);
}
или
try {
return x/x;
} catch (ArithmeticException e) {
return 0;
}
)
EDIT:
Итак, я сделал microbenchmark трех разных решений:
Тайминги для случайных входов int (весь диапазон int) были:
1. 0.268716
2. 0.324449
3. 0.347852
Да, мое глупое решение x/x было быстрее благодаря довольно внушительному запасу. Не удивительно, если учесть, что в нем очень мало 0, и в подавляющем большинстве случаев выполняется быстрый путь.
Тайминги для более интересного случая, когда 50% входов 0:
1. 1.256533
2. 0.321485
3. 0.348999
Наивное решение x==0?0:1
было быстрее примерно на 5%, чем умное (на моей машине). Завтра я попытаюсь сделать разборку, чтобы выяснить, почему.
EDIT2: Итак, разбор для условной версии (за исключением бухгалтерского учета):
testl rsi,rsi
setnz rax
movzbl rax,rax
Разборка для (x | -x) → > 31:
movl rax,rsi
negl rax
orl rax,rsi
sarl rax,#31
Я не думаю, что что-то еще нужно сказать.
Хорошо, самое короткое решение без условного значения, вероятно:
return (i|-i) >>> 31;
Вот решение:
public static int compute(int i)
{
return ((i | (~i + 1)) >> 31) & 1; // return ((i | -i) >> 31) & 1
}
EDIT:
или вы можете сделать его более простым:
public static int compute(int i)
{
return -(-i >> 31); // return -i >>> 31
}
EDIT2:
последнее решение выходит из строя с отрицательными числами. Взгляните на решение @Ed Staub.
EDIT3:
@Orion Adrian OK, вот общее решение:
public static int compute(int i)
{
return (i|-i) >>> java.math.BigInteger.valueOf(Integer.MAX_VALUE).bitLength();
}
int f(int x) {
return Math.abs(Integer.signum(x));
}
Функция signum()
возвращает знак числа как -1, 0 или 1. Таким образом, все, что осталось, должно превратить -1 в 1, что и делает abs
.
Функция signum
реализует его таким образом
return (i >> 31) | (-i >>> 31);
поэтому просто добавьте другую поразрядную операцию для возврата 0
или 1
return ((i >> 31) | (-i >>> 31)) & 1;
Все эти решения, похоже, страдают от порока, требующего различной степени усилий для понимания. Это означает, что программист, который должен позже читать и поддерживать этот код, должен будет тратить ненужные усилия. Это стоит денег.
Выражение
(x == 0)? 0:1
прост и понятен. Это действительно правильный способ сделать это. Использование исключения в обычном прогоне кода совершенно ужасно. Исключения предназначены для обработки обстоятельств, отличных от контроля программистов, а не для обычных рутинных операций.
Интересно, что компилятор превратит это в...
class kata {
public static int f(int x){
return -(Boolean.valueOf(x==0).compareTo(true));
}
public static void main(String[] args) {
System.out.println(f(0));
System.out.println(f(5));
System.out.println(f(-1));
}
}
Этот вопрос сводится к: "Есть ли способ сопоставить логическое значение true, false с int 1,0, не записывая условное выражение".
В Java нет стандартизированной обработки true
как 1
. Наиболее близким является использование -1
. Так как @Ed говорит, тернарный оператор так же краток, как и вы.
Если вы хотите логическое, я думаю:
return x == x >>> 1
Сделал бы это, потому что единственное число, чьи биты набора не перемещаются при сдвиге, равно единице без заданных битов.
Под капотом байт-код фактически использует 1 и 0 для true и false, но я не знаю, как превратить логическое значение языка Java в его соответствующее значение int без какого-либо условного выражения.