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?
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");
}
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
}
}
}
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;
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).
Et ça?
return ((num + "").charAt(0) == '-');
// 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 ...
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:
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
}
}
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é:
int
/Integer
long
/Long
float
/Float
double
/Double
Double.NEGATIVE_INFINITY
Float.NEGATIVE_INFINITY
et false
autrement.
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;
}
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!
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
Si c'est une réponse valide
boolean IsNegative(char[] v) throws NullPointerException, ArrayIndexOutOfBoundException
{
return v[0]=='-';
}
Essayez ceci sans le code: (x-SQRT(x^2))/(2*x)
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.
Non testé, mais illustrant mon idée:
boolean IsNegative<T>(T v) {
return (v & ((T)-1));
}
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);
}
if (((Double)calcYourDouble()).toString().contains("-"))
doThis();
else doThat();
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);
}
Il y a une fonction est la bibliothèque mathématique appelée signnum.
http://www.tutorialspoint.com/Java/lang/math_signum_float.htmhttp://www.tutorialspoint.com/Java/lang/math_signum_double.htm
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;
}
}
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.");
}
C'est facile de faire ça comme
private static boolean isNeg(T l) {
return (Math.abs(l-1)>Math.abs(l));
}
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 ...
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
Écrivez-le en utilisant le conditionnel, puis examinez le code d'assemblage généré.
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)
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.
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));
}
}
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";
}