31 votes

f(int x) { return x == 0 ? 0 : 1 ; } en Java sans conditionnel

Je veux mettre en œuvre f(int x) { return x == 0 ? 0 : 1; } en Java.

En C, j'aurais juste " return !!x; ", mais ! ne fonctionne pas comme ça en Java. Existe-t-il un moyen de le faire sans conditionnelles ? Sans quelque chose d'aussi ridicule qu'une version non laminée de

int ret = 0;
for (int i = 0; i < 32; i++) {
    ret |= ((x & (1 << i)) >>> i);
}

ou

try {
   return x/x;
} catch (ArithmeticException e) {
   return 0;
}

)

EDITAR:

J'ai donc réalisé un microbenchmark de trois solutions différentes :

  1. mon retour x/x attrape la solution,
  2. la solution évidente x==0?0:1, et
  3. Solution d'Ed Staub : (x|-x) >>> 31.

Les temps pour des entrées int aléatoires (toute la gamme int) étaient :

1. 0.268716  
2. 0.324449  
3. 0.347852  

Oui, ma stupide solution x/x était plus rapide d'une marge assez importante. Ce n'est pas très surprenant si l'on considère qu'il y a très peu de 0 dans cette solution et que, dans la grande majorité des cas, la solution rapide est utilisée.

Les timings pour le cas le plus intéressant où 50% des entrées sont à 0 :

1. 1.256533  
2. 0.321485  
3. 0.348999  

Les naïfs x==0?0:1 était plus rapide d'environ 5% que la solution intelligente (sur ma machine). Je vais essayer de faire un peu de désassemblage demain pour découvrir pourquoi.

EDIT2 : Ok, donc le désassemblage pour la version conditionnelle est (sans comptabilité) :

testl rsi,rsi
setnz rax
movzbl rax,rax

Le désassemblage pour (x|-x)>>>31 est :

movl rax,rsi
negl rax
orl rax,rsi
sarl rax,#31

Je pense qu'il n'y a rien d'autre à dire.

42voto

Ed Staub Points 7386

Ok, la solution la plus courte sans conditionnel est probablement :

return (i|-i) >>> 31;

11voto

Eng.Fouad Points 44085

Voici une solution :

public static int compute(int i)
{
    return ((i | (~i + 1)) >> 31) & 1; // return ((i | -i) >> 31) & 1
}

EDITAR:

ou vous pouvez le faire plus simplement :

public static int compute(int i)
{
    return -(-i >> 31); // return -i >>> 31
}

EDIT2 :

La dernière solution échoue avec les nombres négatifs. Jetez un coup d'œil à la solution de @Ed Staub.

EDIT3 :

@Orion Adrian OK, voici une solution générale :

public static int compute(int i)
{
    return (i|-i) >>> java.math.BigInteger.valueOf(Integer.MAX_VALUE).bitLength();
}

8voto

phlogratos Points 3424
int f(int x) {
    return Math.abs(Integer.signum(x));
}

Le site signum() La fonction renvoie le signe du nombre sous la forme -1, 0 ou 1. Il ne reste donc plus qu'à transformer -1 en 1, ce que font abs fait.

7voto

ncmathsadist Points 1208

Toutes ces solutions semblent souffrir du vice d'être plus ou moins difficiles à comprendre. Cela signifie que le programmeur qui devra plus tard lire et maintenir ce code devra déployer des efforts inutiles. Cela coûte de l'argent.

L'expression

(x == 0)? 0:1

est direct et simple à comprendre. C'est vraiment la bonne façon de procéder. L'utilisation d'une exception dans l'exécution ordinaire d'un code est carrément épouvantable. Les exceptions servent à gérer des circonstances indépendantes de la volonté du programmeur, et non à effectuer des opérations de routine ordinaires.

7voto

Yanick Rochon Points 18537

Le site signum l'implémente de la façon suivante

return (i >> 31) | (-i >>> 31);

donc, il suffit d'ajouter une autre opération bit à bit pour retourner 0 o 1

return ((i >> 31) | (-i >>> 31)) & 1;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X