Comparer deux entiers en C ou C ++ sans opérateurs de comparaison

12

Produire le programme le plus court qui prend en entrée deux entiers signés (via stdin ou comme arguments) et affiche 3 sorties différentes selon que le premier nombre est (1) supérieur à, (2) inférieur à ou (3) égal au second nombre.

The Catch

Vous ne pouvez utiliser aucun des éléments suivants dans votre programme:

  • Les opérateurs de comparaison standard: <, >, <=, >=, ==, !=.
  • Tout fichier de bibliothèque à part conio, stdioou iostream.
  • Tout caractère ASCII non ASCII ou non imprimable.

Le gagnant

Le programme avec le plus petit nombre de caractères gagne.

bosquet
la source
Je suppose que l'utilisation de choses comme abs sans inclure le fichier de bibliothèque (parce que le compilateur le sait de toute façon) n'est pas autorisée non plus?
Martin Ender
1
@ MartinBüttner oui, ce serait une hypothèse correcte. :)
bosquet
5
Pourquoi la restriction en C (++)? Si c'est parce que vous voulez que les réponses soient portables malgré la non-portabilité des types de base de C, vous devez le dire. S'il s'agit d'une restriction arbitraire, sachez que les restrictions arbitraires à une langue sont impopulaires sur ce site.
Peter Taylor
8
@PeterTaylor, cela fait partie du défi. Ce serait un jeu de balle très différent si la question était indépendante de la langue. Le restreindre à C / C ++ modifie les stratégies utilisées lors de l'approche du problème. Je reconnais la nécessité que les questions soient ouvertes à la plupart des langues pour promouvoir la participation d'un plus grand nombre de personnes, mais dans ce problème particulier, la restriction au C / C ++ et à leurs opérateurs et méthodes spécifiques fait partie intégrante du défi.
bosquet
1
@EvilTeach oui; si quelque chose n'est pas explicitement interdit dans la question, alors c'est permis.
bosquet

Réponses:

2

53 octets

main(a,b){scanf("%d%d",&a,&b);printf("%ld",0l+a-b);}

Seul le premier caractère de la sortie est pertinent. Les trois sorties différentes sont:

  1. '-' si b> a
  2. '0' si a == b
  3. tout autre caractère si a> b

Il fonctionne pour la plage d'entrée complète de int sur toutes les plates-formes où sizeof (long)> sizeof (int).

Edit: il faut un caractère supplémentaire pour que le cas 3 imprime un '+' uniquement à la place:

main(a,b){scanf("%d%d",&a,&b);printf("%+ld",0l+a-b);}
paradigmsort
la source
6

Peut-être que je manque quelque chose dans les règles, mais ...

81 octets

main(a,b){scanf("%d%d",&a,&b);long long l=a;l-=b;printf("%lld%d",--l>>63,l>>63);}

Ouputs 00si a > b, -10si a == bet -1-1si a < b.

COTO
la source
Aussi courant que soit ce type de code, C ne garantit pas qu'il fonctionne. long longpourrait être supérieur à 64 bits, intpourrait être aussi grand que vous pourriez déborder, le résultat du décalage à droite des valeurs négatives est défini par l'implémentation. Presque toutes les réponses dérivées de C ont des problèmes similaires.
Yann Vernier
1
@YannVernier: Compris. Je suppose qu'une solution à 100% à toute épreuve serait un monstre, car la seule chose que nous pourrions faire en toute sécurité (c'est-à-dire sans décalage ni débordement) est un peu tortueux, et pour le faire en toute sécurité, nous aurions besoin de déterminer la longueur des opérandes en utilisant sizeof.
COTO
6

90 octets

Si nous pouvons utiliser stdio, pourquoi ne pas utiliser ses capacités de formatage pour effectuer une comparaison?

main(a,b){scanf("%d%d",&a,&b);snprintf(&a,2,"%d",b-a);a&=63;putchar(51-!(a-45)-!!(a-48));}

Suppose un encodage compatible ASCII et peu d'endianisme.

72 octets

Les quotients sont arrondis vers zéro mais les décalages vers la droite sont (en pratique) "arrondis vers le bas". C'est un cadeau mort.

main(a,b){scanf("%d%d",&a,&b);a-=b;putchar(a?a|=1,a/2-(a>>1)?60:62:61);}

65 79 octets

Une autre propriété distinctive des nombres négatifs est qu'ils produisent un modulo négatif. Celui-ci ne dépend pas du tout de la représentation entière; ça marche même sur mon grille-pain 8 bits en excès-127! Oh, et puisque nous pouvons utiliser conio, pourquoi ne pas enregistrer deux octets avec putch? Maintenant, si je pouvais seulement trouver ma copie de TurboC ...

main(a,b){scanf("%d%d",&a,&b);long long d=a;d-=b;putch(d?d|=1,d%2-1?60:62:61);}

EDIT : gérer de grandes différences en supposant qu'il long longest plus large que int.

Aune
la source
Je suis sûr que vous avez besoin d'un délimiteur entre les %ds dans votre scanfpour analyser sans ambiguïté deux entiers. Belle idée quand même!
Martin Ender
1
@Martin: Eh bien, cela fonctionne avec GCC, mais je ne sais vraiment pas si c'est de bonne foi.
Ell
Ce que je veux dire, c'est comment distinguer entre les entrées a = 1, b = 23et a = 12, b = 3. N'auriez-vous pas besoin de mettre 123STDIN dans les deux cas?
Martin Ender
1
Comme je l'ai dit, cela semble fonctionner (avec 1 23et 12 3comme entrées).
Ell
2
Ohhh, vous incluez des espaces dans l'entrée. Ouais, je ne suis pas surpris que ça marche, en fait.
Martin Ender
5

64 61 caractères

main(a,b){scanf("%d%d",&a,&b);for(a-=b;a/2;a/=2);putchar(a);}

Imprime les valeurs de caractères -1, 0 et 1 pour respectivement inférieur, égal ou supérieur à.

Cette implémentation repose sur un comportement non défini pour bêtre de type intet pour des entrées en dehors de la plage INT_MIN / 2de INT_MAX / 2. Sur les plates-formes où le débordement signé s'enroule, qu'il s'agisse d'un complément 2s (essentiellement tous) ou d'une amplitude de signe, il échouera pour 25% des paires possibles de paires valides int. Fait intéressant (pour moi en tout cas), cela fonctionnera correctement sur les plateformes où le débordement signé sature.

laindir
la source
Cela ne fonctionnera pas en cas de a-bdébordement.
Dennis
Malheureusement, cela est vrai, mais je ne pouvais pas penser à un moyen indépendant de la plate-forme pour éviter cela sans opérateurs de comparaison. La question ne spécifie pas une plage d'entrées pour lesquelles les résultats doivent être valides. Cette réponse est garantie par la norme de fonctionner pour toutes les entrées entre -(2^14)et 2^14 - 1sur toutes les plates-formes conformes, et elle fonctionnera probablement pour une plage sensiblement plus grande sur la plupart des plates-formes. Toutes les autres réponses à ce stade font des hypothèses sur la taille du type, les tailles relatives des types ou la représentation.
laindir
La question dit deux entiers signés en entrée , donc je dirais que cela doit fonctionner pour toutes les paires. main(a,b)est déjà un comportement indéfini, donc aucune des réponses n'est garantie pour fonctionner. La portabilité peu importe.
Dennis
Vous avez absolument raison concernant le comportement indéfini, donc mon implémentation ne garantit vraiment rien par la norme. J'ajouterai une note indiquant ses limites.
laindir
3

66 102 octets

main(a,b,c,d,e){scanf("%d %d",&a,&b);e=1<<31;c=a&e;d=b&e;putchar(a-b?c&~d?48:d&~c?49:a-b&e?48:49:50);}

Lit les entiers de STDIN et imprime 0(a <b), 1(a> b) ou 2(a == b).

Edit: Maintenant, il devrait également fonctionner pour les différences qui sont trop grandes pour tenir dans un entier 32 bits. Je suis sûr que le ternaire imbriqué peut être raccourci avec un peu plus de magie magique.

Martin Ender
la source
Corrigez-moi si je me trompe, mais je vois un <0 dans votre ternaire intérieur.
suracteur
@overactor fixed
Martin Ender
3

52 octets

Malheureusement, celui-ci ne fonctionne que pour des nombres entiers positifs, mais je pensais que le concept d'utiliser des opérateurs purement arithmétiques était intéressant:

main(a,b){scanf("%d%d",&a,&b);putchar(b%a/b-a%b/a);}

Les sorties:

  • Code ASCII 0xFF: a inférieur à b
  • Code ASCII 0x00: a égal à b
  • Code ASCII 0x01: a supérieur à b
Traumatisme numérique
la source
Si vous n'utilisez que des entiers positifs, putchar(a/b-b/a)c'est beaucoup plus court.
Dennis
@Dennis qui produit différentes sorties, par exemple pour (50,1) et (51,1). Mais j'ai pu raccourcir un peu cependant.
Digital Trauma
1
Oui, je ne pensais pas correctement ...
Dennis
3

 59    54 caractères

54 caractères avec un compilateur comme gcc qui ne rechigne pas à main(x,y):

main(x,y){scanf("%d%d",&x,&y);y-=x;putchar(y>>31|!y);}

59 caractères sinon:

main(){int x,y;scanf("%d%d",&x,&y);y-=x;putchar(y>>31|!y);}

Production:

  • Code ASCII 0x00 si x <y
  • Code ASCII 0xFF si x> y
  • Code ASCII 0x01 si x == y
Todd Lehman
la source
1
Je peux vous assurer, main(x,y)fonctionne dans gcc, alors n'hésitez pas à supprimer ces 5 octets de votre nombre de caractères.
Martin Ender
main (x, y) ne fonctionne pas sur mon gcc. Peut-être qu'une option de compilation est requise. Mais vous pouvez remplacer main (x, y) par x; main (y).
Florian F
2

66 octets

main(a,b){scanf("%d%d",&a,&b);putchar((0l+b-a>>63)-(0l+a-b>>63));}

Imprime l'octet 0x00 if a == b, 0x01 if a < bet 0xff if a > b.

Étant donné que le caractère ASCII non ASCII ou non imprimable dans [mon] programme et si quelque chose n'est pas explicitement interdit dans la question, alors il est autorisé , le caractère non imprimable dans la sortie devrait être tout à fait correct .

Dennis
la source
Ma version précédente ne gérait pas très bien le débordement. Cela fonctionne sur x64 Linux, où longest 64 bits.
Dennis
2

87 caractères

main(a,b,c){scanf("%d%d",&a,&b);c=1<<31;a+=c;b+=c;puts(a^b?(unsigned)a/b?">":"<":"=");}

Utilisation de l'astuce 2 ^ 31 pour convertir en int non signés

Casting de la division sur unsigned pour gérer le bit supérieur en tant que données, pas signer

En utilisant ^ à XOR a et b, quand ils sont égaux, cela renvoie 0

Utilisation de conditions imbriquées (?) Pour obtenir "<", ">" ou "=" pour alimenter les options put ()

Pelle
la source
1

71 octets

main(x,y,z){scanf("%d%d",&x,&y);putchar((z=x-y)?(z&(z>>31))?50:49:51);}

http://ideone.com/uvXm6c

Michael M.
la source
Votre idéone a des parenthèses z=x-yet je suis sûr qu'elles sont nécessaires. Vous pouvez également enregistrer deux caractères en utilisant 49, 50` et 51directement, au lieu d'ajouter 48.
Martin Ender
L'affectation a une priorité inférieure à celle de l'opérateur ternaire: en.cppreference.com/w/c/language/operator_precedence
Martin Ender
Votre code ci-dessus manque un point-virgule, et il échoue pour -2000000000 2000000000, ainsi que toute autre combinaison d'entiers qui provoquent un débordement dans la soustraction.
COTO
1

68 caractères

int main(a,b){scanf("%d%d",&a,&b);putchar(a-b?((unsigned)a-b)>>31:2);}

Place le caractère ASCII 1, 2 ou 3 pour respectivement inférieur, supérieur ou égal.

CompuChip
la source
1
Cela ne fonctionnera pas en cas de a-bdébordement.
Dennis
1

88 89 octets

main(a,b){scanf("%d%d",&a,&b);a+=1<<31;b+=1<<31;for(;a&&b;a--)b--;putchar(a?b?48:49:50);}

Cela commence par ajouter 1<<31( INT_MIN) à a et b, de sorte que 0 correspond maintenant INT_MIN. Ensuite, boucle et décrémente a et b chaque boucle jusqu'à ce que soit 0, puis imprime 0, 1 ou 2 selon que a, b ou les deux sont 0.

120 119 octets

main(a,b,c){scanf("%d%d",&a,&b);c=1<<31;a^=c;b^=c;for(c~=c;!c;c/=2)if(a&c^b&c){putchar(a?48:49);return;}putchar(50);}

Ce n'est pas la solution la plus courte, mais elle pourrait être un peu golfée par un meilleur golfeur que moi. (Ou juste des gens avec plus de connaissance de C que moi)

L'idée est de masquer chaque bit, en commençant par celui de gauche et en vérifiant les inégalités. Le reste devrait s'expliquer. Étant donné que les nombres négatifs commencent par 1 bit, j'inverse d'abord le premier bit avec a^=1<<31.

suracteur
la source
Je ne peux pas tester mes solutions pour le moment, alors n'hésitez pas à signaler des erreurs.
suracteur
La première solution a quelques problèmes: 1. Le ;)smiley heureux devrait être un );smiley triste . 2. a&bteste uniquement si aet bont des bits en commun; vous avez besoin &&.
Dennis
@Dennis, tu as raison, merci.
suracteur
1

Je pense que je ne vais même pas essayer d'écrire du code court. Ce que je vais tenter est d'effectuer cette comparaison d'une manière qui est portable selon la spécification C99.

int a, b;   // Let's assume these are initialized
int sign_a = a ? ((a|7)^2)%2 + ((a|7)^3)%2 : 0;

L'opérateur modulo conserve le signe, mais il peut très bien produire un zéro (y compris un zéro négatif), nous nous assurons donc d'avoir une valeur à la fois impaire et paire à vérifier (même sans savoir si nous utilisons des compléments). Les opérations arithmétiques peuvent déborder, mais pas au niveau du bit, et en veillant à ce qu'il y ait des bits à la fois définis et effacés, nous évitons de convertir par inadvertance notre nombre en zéro négatif ou en valeur d'interruption. Le fait que deux opérations soient nécessaires pour le faire étrangement ne devrait pas avoir d'importance, car la représentation d'interruption possible ne provoque pas de comportement indéfini jusqu'à ce qu'elle soit mise dans une valeur l. Faire l'opération avec le bit 0 basculé garantit que nous obtenons exactement un reste non nul. Armé de la connaissance des deux signes, nous pouvons décider comment procéder à la comparaison.

char result="\0<<>=<>>\0"[4+3*sign_a+sign_b]
if (!result) {   // signs matching means subtraction won't overflow
  int diff=a-b;
  int sign_diff=diff ? (diff|7^2)%2 + (diff|7^3)%2 : 0;
  result = ">=<"[1-sign_diff];
}

Cette méthode peut être l'une des rares à permettre d'extraire le signe d'un zéro négatif entier. Nous résolvons cela en vérifiant explicitement zéro. Si nous jouions au golf pour de vrai, nous pourrions bien sûr permettre la comparaison de deux zéros pour effectuer également la soustraction.

Yann Vernier
la source
1

C 80 caractères

a,b,c=1<<31;main(){scanf("%d%d",&a,&b);putchar(a^b?(a&c^b&c?a:a-b)&c?60:62:61);}

Il imprime '<', '>' ou '=', comme il se doit.

C 63 caractères

Une nouvelle approche:

a;main(b){scanf("%d%d",&a,&b);putchar(50+(0L+a-b>>42)+!(a-b));}

Imprime «1», «2» ou «3».

Florian F
la source
1

En 64 caractères sans stdio.h

a,b;main(){scanf("%d%d",&a,&b);puts((a-b)>>31?"<":a^b?">":"=");}

affiche '>' si a> b, '<' si a <b, '=' si a == b int débordement est UB. Ne débordez pas.

// declare a and b as ints
a,b;

// defaults to int
main()
{
  scanf("%d%d",&a,&b);
   /*
    * (a-b)>>31 signbit of a-b
    * a^b a xor b -> 0 if they are equal
    */
  puts(((a-b)>>31) ? "<" : (a^b) ? ">" : "=");
}
ViralTaco_
la source