Compare dos enteros en C o C ++ sin operadores de comparación


12

Produzca el programa más corto que tome dos enteros con signo como entrada (a través de stdin o como argumentos) y muestre 3 salidas diferentes dependiendo de si el primer número es (1) mayor que, (2) menor que o (3) igual al segundo número.

La captura

No puede utilizar ninguno de los siguientes en su programa:

  • Los operadores de comparación estándar: <, >, <=, >=, ==, !=.
  • Cualquier archivo de biblioteca, aparte de conio, stdioo iostream.
  • Cualquier carácter ASCII no ASCII o no imprimible.

El ganador

El programa con el menor número de caracteres gana.


Supongo que usar cosas como abs sin incluir el archivo de la biblioteca (porque el compilador lo sabe de todos modos) ¿tampoco está permitido?
Martin Ender

1
@ MartinBüttner sí, esa sería una suposición correcta. :)
arboleda

55
¿Por qué la restricción a C (++)? Si es porque desea que las respuestas sean portátiles a pesar de la inportabilidad de los tipos básicos de C, entonces debe indicarlo. Si se trata de una restricción arbitraria, debe tener en cuenta que las restricciones arbitrarias a un idioma no son populares en este sitio.
Peter Taylor

8
@PeterTaylor es parte del desafío. Sería un juego de pelota muy diferente si la pregunta fuera agnóstica del lenguaje. Restringirlo a C / C ++ cambia las estrategias utilizadas al abordar el problema. Reconozco la necesidad de que las preguntas estén abiertas a la mayoría de los lenguajes para promover la participación de más personas, pero en este problema particular, la restricción a C / C ++ y sus operadores y métodos específicos es una parte integral del desafío.
arboleda

1
@EvilTeach sí; Si algo no está explícitamente prohibido en la pregunta, entonces está permitido.
arboleda

Respuestas:


2

53 bytes

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

Solo el primer carácter de la salida es relevante. Las tres salidas diferentes son:

  1. '-' si b> a
  2. '0' si a == b
  3. cualquier otro caracter si a> b

Funciona para el rango de entrada completo de int en todas las plataformas donde sizeof (long)> sizeof (int).

Editar: cuesta un carácter adicional hacer que el caso 3 imprima un '+' de forma exclusiva:

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

6

Tal vez me falta algo en las reglas, pero ...

81 bytes

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 == by -1-1si a < b.


Tan común como este tipo de código es, C no garantiza que funcione. long longpodría ser más de 64 bits, intpodría ser tan grande que podría desbordarse, el resultado del desplazamiento negativo de los valores negativos está definido por la implementación. Casi todas las respuestas derivadas de C tienen problemas similares.
Yann Vernier

1
@YannVernier: Entendido. Supongo que una solución 100% infalible sería un monstruo, ya que lo único que podríamos hacer de manera segura (es decir, sin cambios o desbordamientos) es un poco retorcido, y para hacerlo de manera segura, tendríamos que determinar la longitud de los operandos usando sizeof.
COTO

6

90 bytes

Si podemos usar stdio, ¿por qué no usar sus capacidades de formateo para realizar la comparación?

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

Asume codificación compatible con ASCII y poca endianidad.

72 bytes

Los cocientes se redondean hacia cero, pero los desplazamientos a la derecha son (en la práctica) "redondeados hacia abajo". Eso es un regalo muerto.

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

sesenta y cinco 79 bytes

Otra propiedad distintiva de los números negativos es que producen un módulo negativo. Este no depende en absoluto de la representación entera; ¡incluso funciona en mi tostadora con exceso de 127 bits de 8 bits! Ah, y como podemos usar conio, ¿por qué no guardar dos bytes con putch? Ahora, si solo pudiera encontrar mi copia 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);}

EDITAR : Manejar grandes diferencias asumiendo que long longes más amplio que int.


Estoy bastante seguro de que necesita un delimitador entre las %ds en su scanfpara analizar sin ambigüedad dos enteros. Buena idea sin embargo!
Martin Ender

1
@ Martin: Bueno, funciona con GCC, pero realmente no estoy seguro de si es de buena fe.
Ell

Lo que quiero decir es, ¿cómo se distingue entre entradas a = 1, b = 23y a = 12, b = 3. ¿No necesitarías ponerte 123STDIN en cualquier caso?
Martin Ender

1
Como dije, parece funcionar (con 1 23y 12 3como entradas).
Ell

2
Ohhh, sí incluye espacios en la entrada. Sí, no me sorprende que funcione, en realidad.
Martin Ender

5

64 61 caracteres

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

Imprime los valores de caracteres de -1, 0 y 1 para menor, igual o mayor que, respectivamente.

Esta aplicación se basa en un comportamiento indefinido para bser de tipo inty para las entradas fuera del rango INT_MIN / 2a INT_MAX / 2. En plataformas donde el desbordamiento firmado se envuelve, ya sea 2s-complemento (básicamente todos) o magnitud de signo, fallará para el 25% de los posibles pares de válidos int. Curiosamente (para mí de todos modos), funcionará correctamente en plataformas donde se satura el desbordamiento firmado.


Esto no funcionará si se a-bdesborda.
Dennis

Lamentablemente eso es cierto, pero no podría pensar en ninguna forma agnóstica de plataforma para evitar esto sin operadores de comparación. La pregunta no especifica un rango de entradas para las cuales los resultados deben ser válidos. El estándar garantiza que esta respuesta funcione para todas las entradas entre -(2^14)y 2^14 - 1en todas las plataformas compatibles, y probablemente funcionará para un rango sustancialmente mayor en la mayoría de las plataformas. Todas las otras respuestas en este punto hacen suposiciones sobre el tamaño del tipo, los tamaños relativos de los tipos o la representación.
laindir

La pregunta dice dos enteros con signo como entrada , por lo que diría que tiene que funcionar para todos los pares. main(a,b)es un comportamiento indefinido, por lo que ninguna de las respuestas está garantizada para funcionar. No importa la portabilidad.
Dennis

Tiene toda la razón con respecto al comportamiento indefinido, por lo que mi implementación realmente no garantiza nada según el estándar. Agregaré una nota indicando cuáles son sus limitaciones.
laindir

3

66 102 bytes

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);}

Lee los enteros de STDIN e imprime 0(a <b), 1(a> b) o 2(a == b).

Editar: ahora también debería funcionar para diferencias que son demasiado grandes para caber en un entero de 32 bits. Estoy seguro de que ese ternario anidado se puede acortar con algo más de magia de bits.


Corrígeme si me equivoco, pero veo un <0 en tu ternario interno.
overactor

@overactor fijo
Martin Ender

3

52 bytes

Lamentablemente, este solo funciona para enteros positivos, pero pensé que el concepto de usar operadores puramente aritméticos era interesante:

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

Salidas:

  • Código ASCII 0xFF: a menor que b
  • Código ASCII 0x00: a igual a b
  • Código ASCII 0x01: a mayor que b

Si solo va por números enteros positivos, putchar(a/b-b/a)es mucho más corto.
Dennis

@Dennis que produce diferentes salidas, por ejemplo para (50,1) y (51,1). Pero pude acortar un poco sin embargo.
Trauma digital

1
Sí, no estaba pensando correctamente ...
Dennis

3

 59    54 caracteres

54 caracteres con un compilador como gcc que no se opone a main(x,y):

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

59 caracteres de lo contrario:

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

Salida:

  • Código ASCII 0x00 si x <y
  • Código ASCII 0xFF si x> y
  • Código ASCII 0x01 si x == y

1
Te puedo asegurar que main(x,y)funciona en gcc, así que siéntete libre de eliminar esos 5 bytes de tu cuenta de caracteres.
Martin Ender

main (x, y) no funciona en mi gcc. Tal vez se requiere una opción de compilador. Pero puede reemplazar main (x, y) por x; main (y).
Florian F

2

66 bytes

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

Imprime el byte 0x00 if a == b, 0x01 if a < by 0xff if a > b.

Dado que los caracteres ASCII no ASCII o no imprimibles en [mi] programa y si algo no está explícitamente prohibido en la pregunta, entonces está permitido , el carácter no imprimible en la salida debería estar completamente bien.


Mi versión anterior no manejó muy bien el desbordamiento. Esto funciona en Linux x64, donde longes de 64 bits.
Dennis

2

87 caracteres

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

Usando el truco 2 ^ 31 para convertir a int sin firmar

Convertir la división en sin signo para manejar el bit superior como datos, no firmar

Usando ^ para XOR a y b, cuando son iguales esto devuelve 0

Usando condicionales anidados (?) Para obtener "<", ">", o "=" para alimentar a put ()


1

71 bytes

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

http://ideone.com/uvXm6c


Tu ideona tiene paréntesis z=x-yy estoy bastante seguro de que son necesarios. También puede guardar dos caracteres usando 49, 50` y 51directamente, en lugar de agregar 48.
Martin Ender

La asignación tiene una precedencia más baja que el operador ternario: en.cppreference.com/w/c/language/operator_precedence
Martin Ender

A su código anterior le falta un punto y coma, y ​​falla -2000000000 2000000000, así como cualquier otra combinación de enteros que provoque un desbordamiento en la resta.
COTO

1

68 caracteres

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

Pone el carácter ASCII 1, 2 o 3 por menor que, mayor que o igual, respectivamente.


1
Esto no funcionará si se a-bdesborda.
Dennis

1

88 89 bytes

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);}

Esto comienza agregando 1<<31( INT_MIN) a a y b, de modo que 0 ahora corresponde INT_MIN. Luego repite y decrementa ayb cada ciclo hasta que sea 0, luego imprime 0, 1 o 2 dependiendo de si a, b o ambos son 0.

120 119 bytes

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);}

No es la solución más corta como es, pero podría ser menos golfista por un mejor golfista que yo. (O simplemente personas con más conocimiento de C que yo)

La idea es enmascarar cada bit, comenzando por el izquierdo y verificando la desigualdad. El resto debería explicarse por sí mismo. Como los números negativos comienzan con 1 bit, primero invierto el primer bit con a^=1<<31.


No puedo probar mis soluciones por el momento, así que siéntase libre de señalar errores.
overactor

La primera solución tiene un par de problemas: 1. La ;)carita feliz debería ser una );carita triste . 2. a&bsolo prueba si ay btiene bits en común; lo que necesita &&.
Dennis

@ Dennis, tienes razón, gracias.
overactor

1

Creo que ni siquiera voy a intentar escribir código corto. Lo que intentaré es realizar esta comparación de una manera que sea portátil según la especificación C99.

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

El operador de módulo conserva el signo, pero bien puede producir un cero (incluido el cero negativo), por lo que nos aseguramos de que tengamos un valor par e impar para que lo verifique (incluso sin saber si estamos usando un complemento). Las operaciones aritméticas pueden desbordarse, pero los bits no lo harán, y al asegurarnos de que los bits estén configurados y borrados, evitamos convertir nuestro número en cero negativo o en un valor de trampa sin darse cuenta. El hecho de que se requieran dos operaciones para hacerlo de manera extraña no debería importar, porque la posible representación de trampa no causa un comportamiento indefinido hasta que se pone en un valor. Hacer la operación con el bit 0 activado garantiza que obtengamos exactamente un resto distinto de cero. Armados con el conocimiento de ambos signos, podemos decidir cómo proceder con la comparación.

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];
}

Este método puede ser uno de los pocos que permiten extraer el signo de un cero negativo entero. Resolvemos eso comprobando explícitamente el cero. Si estuviéramos jugando al golf de verdad, podríamos permitir la comparación de dos ceros para realizar también la resta.


1

C 80 caracteres

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);}

Imprime '<', '>' o '=', como debería.

C 63 caracteres

Un nuevo enfoque:

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

Imprime '1', '2' o '3'.


1

En 64 caracteres sin stdio.h

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

imprime '>' si a> b, '<' si a <b, '=' si a == b int desbordamiento es UB. Simplemente no se desborde.

// 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) ? ">" : "=");
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.