web-dev-qa-db-fra.com

Comment déterminer si un nombre est positif ou négatif?

Lors d’une entrevue, on m’a demandé comment déterminer si un nombre était positif ou négatif. Les règles sont que nous ne devrions pas utiliser d'opérateurs conditionnels tels que < et >, des fonctions Java intégrées (telles que substring, indexOf, charAt et startsWith), aucune expression rationnelle ou API.

J'ai fait quelques devoirs à ce sujet et le code est donné ci-dessous, mais cela ne fonctionne que pour le type entier. Mais ils m'ont demandé d'écrire un code générique qui fonctionne pour float, double et long.

 // This might not be better way!!

 S.O.P ((( number >> 31 ) & 1) == 1 ? "- ve number " : "+ve number );

des idées de votre part?

56
Dead Programmer

Les cas entiers sont faciles. Le double cas est plus compliqué, jusqu'à ce que vous vous souveniez de l'infini.

Remarque: Si vous considérez les doubles constantes comme «des parties de l'api», vous pouvez les remplacer par des expressions débordantes telles que 1E308 * 2.

int sign(int i) {
    if (i == 0) return 0;
    if (i >> 31 != 0) return -1;
    return +1;
}
int sign(long i) {
    if (i == 0) return 0;
    if (i >> 63 != 0) return -1;
    return +1;
}
int sign(double f) {
    if (f != f) throw new IllegalArgumentException("NaN");
    if (f == 0) return 0;
    f *= Double.POSITIVE_INFINITY;
    if (f == Double.POSITIVE_INFINITY) return +1;
    if (f == Double.NEGATIVE_INFINITY) return -1;

    //this should never be reached, but I've been wrong before...
    throw new IllegalArgumentException("Unfathomed double");
}
66
Craig Gidney

Ce qui suit est une approche terrible qui pourrait vous faire virer à n'importe quel emploi ... 

Cela dépend du fait que vous obteniez une exception de débordement de pile [ou quel que soit ce que Java appelle cela] ... Et cela ne fonctionnerait que pour les nombres positifs qui ne s'écartent pas de 0 comme un fou. 

Les nombres négatifs suffisent, car vous déborderiez trop en positif, puis vous obtiendrez éventuellement une exception de débordement de pile [qui renverrait faux, ou "oui, c'est négatif"]

Boolean isPositive<T>(T a)
{
  if(a == 0) return true;
  else
  {
    try
    {
      return isPositive(a-1);
    }catch(StackOverflowException e)
    {
      return false; //It went way down there and eventually went kaboom
    }
  }
}
36
Warty

Cela ne fonctionne que pour tout sauf la [0..2]

boolean isPositive = (n % (n - 1)) * n == n;

Vous pouvez faire une meilleure solution comme ceci (fonctionne sauf pour [0..1])

boolean isPositive = ((n % (n - 0.5)) * n) / 0.5 == n;

Vous pouvez obtenir une meilleure précision en modifiant la partie 0.5 avec quelque chose comme 2 ^ m (m entier):

boolean isPositive = ((n % (n - 0.03125)) * n) / 0.03125 == n;
17
nanda

Vous pouvez faire quelque chose comme ça:

((long) (num * 1E308 * 1E308) >> 63) == 0 ? "+ve" : "-ve"

L'idée principale ici est que nous choisissons une position longue et vérifions la valeur du bit le plus significatif. Comme un double/float compris entre -1 et 0 sera arrondi à zéro lorsqu’il est lancé sur une longueur, nous multiplions par de grands doubles afin qu’un float/double négatif soit inférieur à -1. Deux multiplications sont nécessaires en raison de l'existence de subnormals (il n'a pas vraiment besoin d'être aussi gros).

8
Nabb

Et ça?

return ((num + "").charAt(0) == '-');
4
Beep beep
// Returns 0 if positive, nonzero if negative
public long sign(long value) {
    return value & 0x8000000000000000L;
}

Appelez comme:

long val1 = ...;
double val2 = ...;
float val3 = ...;
int val4 = ...;

sign((long) valN);

Casting de double/float/integer à long devrait conserver le signe, sinon la valeur réelle ...

3
Steven Schlansker

Vous dites

nous ne devrions pas utiliser d'opérateurs conditionnels

Mais ceci est une astuce, car == est aussi un opérateur conditionnel. Il en existe également une intégrée dans les boucles ? :, while et for. Ainsi, presque tout le monde n’a pas réussi à fournir une réponse répondant à toutes les exigences.

Le seul moyen de créer une solution sans opérateur conditionnel consiste à utiliser une table de recherche plutôt que l'une des solutions de quelques personnes pouvant être résumées à 0/1 ou à un caractère, avant qu'une condition ne soit remplie.

Voici les réponses qui, à mon avis, pourraient fonctionner par rapport à une table de recherche:

  • Nabb
  • Steven Schlansker
  • Dennis Cheung
  • Gary Rowe
3

Cette solution utilise le module. Et oui, cela fonctionne aussi pour 0.5 (les tests sont en dessous, dans la méthode principale).

public class Num {

    public static int sign(long x) {
        if (x == 0L || x == 1L) return (int) x;
        return x == Long.MIN_VALUE || x % (x - 1L) == x ? -1 : 1;
    }

    public static int sign(double x) {
        if (x != x) throw new IllegalArgumentException("NaN");
        if (x == 0.d || x == 1.d) return (int) x;
        if (x == Double.POSITIVE_INFINITY) return 1;
        if (x == Double.NEGATIVE_INFINITY) return -1;
        return x % (x - 1.d) == x ? -1 : 1;
    }

    public static int sign(int x) {
        return Num.sign((long)x);
    }

    public static int sign(float x) {
        return Num.sign((double)x);
    }

    public static void main(String args[]) {

        System.out.println(Num.sign(Integer.MAX_VALUE)); // 1
        System.out.println(Num.sign(1)); // 1
        System.out.println(Num.sign(0)); // 0
        System.out.println(Num.sign(-1)); // -1
        System.out.println(Num.sign(Integer.MIN_VALUE)); // -1

        System.out.println(Num.sign(Long.MAX_VALUE)); // 1
        System.out.println(Num.sign(1L)); // 1
        System.out.println(Num.sign(0L)); // 0
        System.out.println(Num.sign(-1L)); // -1
        System.out.println(Num.sign(Long.MIN_VALUE)); // -1

        System.out.println(Num.sign(Double.POSITIVE_INFINITY)); // 1
        System.out.println(Num.sign(Double.MAX_VALUE)); // 1
        System.out.println(Num.sign(0.5d)); // 1
        System.out.println(Num.sign(0.d)); // 0
        System.out.println(Num.sign(-0.5d)); // -1
        System.out.println(Num.sign(Double.MIN_VALUE)); // -1
        System.out.println(Num.sign(Double.NEGATIVE_INFINITY)); // -1

        System.out.println(Num.sign(Float.POSITIVE_INFINITY)); // 1
        System.out.println(Num.sign(Float.MAX_VALUE)); // 1
        System.out.println(Num.sign(0.5f)); // 1
        System.out.println(Num.sign(0.f)); // 0
        System.out.println(Num.sign(-0.5f)); // -1
        System.out.println(Num.sign(Float.MIN_VALUE)); // -1
        System.out.println(Num.sign(Float.NEGATIVE_INFINITY)); // -1
        System.out.println(Num.sign(Float.NaN)); // Throws an exception

    }
}
2
Saul

Ce code couvre tous les cas et types:

public static boolean isNegative(Number number) {
    return (Double.doubleToLongBits(number.doubleValue()) & Long.MIN_VALUE) == Long.MIN_VALUE;
}

Cette méthode accepte toutes les classes de wrapper (Integer, Long, Float et Double) et permet d'auto-boxing n'importe quel type numérique primitif (int, long, float et double) et la vérifie simplement avec le bit le plus élevé est le bit de signe, est défini.

Il retourne true lorsque l'un des éléments suivants est passé:

  • tout négatif int/Integer
  • tout négatif long/Long
  • tout négatif float/Float
  • tout négatif double/Double
  • Double.NEGATIVE_INFINITY
  • Float.NEGATIVE_INFINITY

et false autrement.

2
Bohemian

une autre option à laquelle je pouvais penser 

private static boolean isPositive(Object numberObject) {
Long number = Long.valueOf(numberObject.toString());
return Math.sqrt((number * number)) != number;
}

 private static boolean isPositive(Object numberObject) {
Long number = Long.valueOf(numberObject.toString());
long signedLeftShifteredNumber = number << 1; // Signed left shift
long unsignedRightShifterNumber = signedLeftShifteredNumber >>> 1; // Unsigned right shift
return unsignedRightShifterNumber == number;
}
1
Siri

Je pense qu'il existe une solution très simple:

public boolean isPositive(int|float|double|long i){
    return (((i-i)==0)? true : false);
}

dis-moi si je me trompe!

1
HowHigH

Cela me semble arbitraire parce que je ne sais pas comment vous obtiendriez le nombre comme n'importe quel type, mais qu'en est-il de vérifier Abs (nombre)! = Nombre? Peut-être && nombre! = 0

1
Jason Goemaat

Si c'est une réponse valide

boolean IsNegative(char[] v) throws NullPointerException, ArrayIndexOutOfBoundException
{ 
  return v[0]=='-'; 
} 
1
Dennis C

Essayez ceci sans le code: (x-SQRT(x^2))/(2*x)

1
Next

Les entiers sont triviaux; tu le sais déjà. Le problème fondamental est de savoir comment gérer les valeurs en virgule flottante. À ce stade, vous devez en savoir un peu plus sur le fonctionnement réel des valeurs en virgule flottante.

La clé est Double.doubleToLongBits () , qui vous permet d’obtenir la représentation IEEE du nombre. (La méthode est vraiment une distribution directe sous le capot, avec un peu de magie pour traiter les valeurs NaN.) Une fois qu'un double a été converti en long, vous pouvez simplement utiliser 0x8000000000000000L comme masque pour sélectionner le bit de signe; si zéro, la valeur est positive, et si un, c'est négatif.

1
Donal Fellows

Non testé, mais illustrant mon idée:

boolean IsNegative<T>(T v) {
  return (v & ((T)-1));
}
1
Will

Celui-ci est approximativement basé sur la réponse de ItzWarty, mais il tourne dans le temps de connexion! Avertissement: ne fonctionne que pour les entiers.

Boolean isPositive(int a)
{
  if(a == -1) return false;
  if(a == 0) return false;
  if(a == 1) return true;
  return isPositive(a/2);
}
1
Clark Gaebel
if (((Double)calcYourDouble()).toString().contains("-"))
        doThis();
else doThat();
0
nachtwezen

Génériques combinés avec double API. Je suppose que c'est un peu de la triche, mais au moins nous n'avons besoin que d'une méthode:

static <T extends Number> boolean isNegative(T number)
{       
    return ((number.doubleValue() * Double.POSITIVE_INFINITY) == Double.NEGATIVE_INFINITY);
}
0
shrini1000
0
thinklarge

Cette solution n'utilise pas d'opérateurs conditionnels, mais repose sur la capture de deux excpetions.

Une erreur de division équivaut à ce que le nombre initial soit "négatif". Sinon, le nombre finira par tomber de la planète et lancera une exception StackOverFlow si elle est positive. 

public static boolean isPositive( f)
       {
           int x;
           try {
               x = 1/((int)f + 1);
               return isPositive(x+1);
           } catch (StackOverFlow Error e) {
               return true;

           } catch (Zero Division Error e) {
               return false;
           }


   }
0
l337x911

Pourquoi ne pas obtenir la racine carrée du nombre? Si son négatif - Java va jeter une erreur et nous allons le gérer.

         try {
            d = Math.sqrt(THE_NUMBER);
         }
         catch ( ArithmeticException e ) {
            console.putln("Number is negative.");
         }
0
DreamWave

C'est facile de faire ça comme

private static boolean isNeg(T l) {
        return (Math.abs(l-1)>Math.abs(l));
 }
0
jamb

Qu'en est-il de ce qui suit?

T sign(T x) {
    if(x==0) return 0;
    return x/Math.abs(x);
}

Devrait fonctionner pour chaque type T ...

Alternativement, on peut définir abs (x) comme Math.sqrt (x * x), Et si c'est également tricher, implémentez votre propre fonction racine carrée ...

0

Si vous êtes autorisé à utiliser "==" comme cela semble être le cas, vous pouvez le faire en tirant parti du fait qu'une exception sera déclenchée si un index de tableau est hors limites. Le code est pour le double, mais vous pouvez convertir n'importe quel type numérique en double (dans ce cas, la perte de précision éventuelle n'aurait aucune importance).

J'ai ajouté des commentaires pour expliquer le processus (apportez la valeur dans] -2.0; -1.0] union [1.0; 2.0 [) et un petit pilote d'essai également.

class T {

   public static boolean positive(double f)
   {
       final boolean pos0[] = {true};
       final boolean posn[] = {false, true};

       if (f == 0.0)
           return true;

       while (true) {

           // If f is in ]-1.0; 1.0[, multiply it by 2 and restart.
           try {
               if (pos0[(int) f]) {
                   f *= 2.0;
                   continue;
               }
           } catch (Exception e) {
           }

           // If f is in ]-2.0; -1.0] U [1.0; 2.0[, return the proper answer.
           try {
               return posn[(int) ((f+1.5)/2)];
           } catch (Exception e) {
           }

           // f is outside ]-2.0; 2.0[, divide by 2 and restart.
           f /= 2.0;

       }

   }

   static void check(double f)
   {
       System.out.println(f + " -> " + positive(f));
   }

   public static void main(String args[])
   {
       for (double i = -10.0; i <= 10.0; i++)
           check(i);
       check(-1e24);
       check(-1e-24);
       check(1e-24);
       check(1e24);
   }

La sortie est:

-10.0 -> false
-9.0 -> false
-8.0 -> false
-7.0 -> false
-6.0 -> false
-5.0 -> false
-4.0 -> false
-3.0 -> false
-2.0 -> false
-1.0 -> false
0.0 -> true
1.0 -> true
2.0 -> true
3.0 -> true
4.0 -> true
5.0 -> true
6.0 -> true
7.0 -> true
8.0 -> true
9.0 -> true
10.0 -> true
-1.0E24 -> false
-1.0E-24 -> false
1.0E-24 -> true
1.0E24 -> true
0
Samuel Tardieu

Écrivez-le en utilisant le conditionnel, puis examinez le code d'assemblage généré.

0
jeffo

Je ne sais pas comment exactement Java convertit les valeurs numériques, mais la réponse est assez simple, si elle est mise en pseudocode (je vous laisse les détails):

sign(x) := (x == 0) ? 0 : (x/x)
0
back2dos

Pas efficace, mais je suppose que ce n'est pas important ici: (Je suis aussi un peu rouillé avec Java, j'espère que c'est une syntaxe plus ou moins correcte.)

boolean isPositive = false;

int n = (int)(x * x);
while (n-- != 0)
{
    if ((int)(--x) == 0)
    {
        isPositive = true;
        break;
    }
}

Cela devrait fonctionner, car x sera décrémenté au plus x * x fois (toujours un nombre positif) et si x n'est jamais égal à 0, il doit alors être négatif. Si x, en revanche, est égal à 0 à un moment donné, cela doit être positif.

Notez que cela donnerait isPositive à false pour 0.

P.S .: Certes, cela ne fonctionnera pas avec de très grands nombres, car (int)(x * x) déborderait.

0
stakx

Bien, profitant du casting (puisque nous ne nous soucions pas de la valeur réelle), peut-être que ceci fonctionnerait. Gardez à l'esprit que les implémentations réelles ne violent pas les règles de l'API. J'ai édité ceci pour rendre les noms de méthodes un peu plus évidents et à la lumière du commentaire de @chris sur le domaine à problèmes {-1, + 1}. Essentiellement, ce problème ne semble pas pouvoir être résolu sans le recours aux méthodes API dans Float ou Double qui référencent la structure de bits native des primitives float et double.

Comme tout le monde a dit: question d'entrevue stupide. Grr.

public class SignDemo {

  public static boolean isNegative(byte x) {
    return (( x >> 7 ) & 1) == 1;
  }

  public static boolean isNegative(short x) {
    return (( x >> 15 ) & 1) == 1;
  }

  public static boolean isNegative(int x) {
    return (( x >> 31 ) & 1) == 1;
  }

  public static boolean isNegative(long x) {
    return (( x >> 63 ) & 1) == 1;
  }

  public static boolean isNegative(float x) {
    return isNegative((int)x);
  }

  public static boolean isNegative(double x) {
    return isNegative((long)x);
  }

  public static void main(String[] args) {


    // byte
    System.out.printf("Byte %b%n",isNegative((byte)1));
    System.out.printf("Byte %b%n",isNegative((byte)-1));

    // short
    System.out.printf("Short %b%n",isNegative((short)1));
    System.out.printf("Short %b%n",isNegative((short)-1));

    // int
    System.out.printf("Int %b%n",isNegative(1));
    System.out.printf("Int %b%n",isNegative(-1));

    // long
    System.out.printf("Long %b%n",isNegative(1L));
    System.out.printf("Long %b%n",isNegative(-1L));

    // float
    System.out.printf("Float %b%n",isNegative(Float.MAX_VALUE));
    System.out.printf("Float %b%n",isNegative(Float.NEGATIVE_INFINITY));

    // double
    System.out.printf("Double %b%n",isNegative(Double.MAX_VALUE));
    System.out.printf("Double %b%n",isNegative(Double.NEGATIVE_INFINITY));

    // interesting cases
    // This will fail because we can't get to the float bits without an API and
    // casting will round to zero
    System.out.printf("{-1,1} (fail) %b%n",isNegative(-0.5f));

  }

}
0
Gary Rowe

Deux solutions simples. Fonctionne également pour les infinis et les nombres -1 <= r <= 1 Renverra "positif" pour NaNs.

String positiveOrNegative(double number){
    return (((int)(number/0.0))>>31 == 0)? "positive" : "negative";
}

String positiveOrNegative(double number){
    return (number==0 || ((int)(number-1.0))>>31==0)? "positive" : "negative";
}
0
IvarR