Porównaj dwie liczby całkowite w C lub C ++ bez operatorów porównania

13

Utwórz najkrótszy program, który pobiera dwie liczby całkowite ze znakiem jako dane wejściowe (przez standardowe lub jako argumenty) i wyświetla 3 różne dane wyjściowe w zależności od tego, czy pierwsza liczba jest (1) większa niż, (2) mniejsza niż, czy (3) równa drugiej numer.

The Catch

W swoim programie nie możesz używać żadnego z poniższych:

  • Standardowe operatory porównania: <, >, <=, >=, ==, !=.
  • Każdy plik biblioteki oprócz conio, stdiolub iostream.
  • Dowolny znak ASCII inny niż ASCII lub niedrukowalny.

Zwycięzca

Program z najmniejszą liczbą znaków wygrywa.

gaj
źródło
Przypuszczam, że używanie rzeczy takich jak abs bez dołączania pliku biblioteki (ponieważ kompilator i tak to wie) również nie jest dozwolone?
Martin Ender
1
@ MartinBüttner tak, to byłoby prawidłowe założenie. :)
Grove
5
Dlaczego ograniczenie do C (++)? Jeśli to dlatego, że chcesz, aby odpowiedzi były przenośne pomimo braku możliwości przenoszenia podstawowych typów języka C, powinieneś to zaznaczyć. Jeśli jest to dowolne ograniczenie, należy pamiętać, że arbitralne ograniczenia dotyczące jednego języka nie są popularne na tej stronie.
Peter Taylor,
8
@PeterTaylor jest częścią wyzwania. Byłoby to zupełnie inna gra w piłkę, gdyby pytanie było niezależne od języka. Ograniczenie go do C / C ++ zmienia strategie stosowane przy podejściu do problemu. Rozumiem potrzebę otwarcia pytań na większość języków w celu promowania uczestnictwa większej liczby osób, ale w tym konkretnym problemie ograniczenie do C / C ++ oraz ich konkretnych operatorów i metod stanowi integralną część wyzwania.
Grove
1
@EvilTeach tak; jeśli coś nie jest wyraźnie zabronione w pytaniu, jest to dozwolone.
Grove

Odpowiedzi:

2

53 bajty

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

Istotny jest tylko pierwszy znak wyniku. Trzy różne wyjścia to:

  1. „-” jeśli b> a
  2. „0”, jeżeli a == b
  3. dowolny inny znak, jeżeli a> b

Działa dla pełnego zakresu wejściowego int na wszystkich platformach, na których sizeof (long)> sizeof (int).

Edycja: kosztuje jeden dodatkowy znak, aby przypadek 3 wydrukował jednoznacznie „+” zamiast:

main(a,b){scanf("%d%d",&a,&b);printf("%+ld",0l+a-b);}
paradigmsort
źródło
6

Może brakuje mi czegoś w przepisach, ale ...

81 bajtów

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

Ouputs 00jeśli a > b, -10jeśli a == bi -1-1jeśli a < b.

COTO
źródło
Choć kod tego rodzaju jest powszechny, C nie gwarantuje, że zadziała. long longmoże mieć więcej niż 64 bity, intmoże być tak duży, że możesz się przepełnić, wynikiem przesunięcia w prawo wartości ujemnych jest zdefiniowana implementacja. Prawie wszystkie odpowiedzi pochodzące od C mają podobne problemy.
Yann Vernier
1
@YannVernier: Zrozumiano. Domyślam się, że w 100% niezawodne rozwiązanie byłoby potworem, ponieważ jedyną rzeczą, którą moglibyśmy zrobić bezpiecznie (tj. Bez przesuwania lub przelewania), było trochę kręcenie się i aby to zrobić bezpiecznie, musieliśmy określić długość operandów za pomocą sizeof.
COTO,
6

90 bajtów

Jeśli możemy użyć stdio, dlaczego nie skorzystać z jego możliwości formatowania w celu przeprowadzenia porównania?

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

Zakłada kodowanie kompatybilne z ASCII i małą trwałość.

72 bajty

Ilości są zaokrąglane w kierunku zera, ale przesunięcia w prawo są (w praktyce) „zaokrąglane w dół”. To martwa gratka.

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

65 79 bajtów

Inną wyróżniającą właściwością liczb ujemnych jest to, że wytwarzają one ujemne modulo. Ten w ogóle nie zależy od reprezentacji liczb całkowitych; działa nawet na moim 8-bitowym tosterze z nadmiarem 127! Aha, a skoro możemy użyć conio, dlaczego nie zapisać dwóch bajtów putch? Teraz gdybym tylko mógł znaleźć swoją kopię 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);}

EDYCJA : Obsługa dużych różnic przy założeniu, że long longjest szersza niż int.

Łokieć
źródło
Jestem pewien, że potrzebujesz separatora między literami %dw swoim, scanfaby jednoznacznie przeanalizować dwie liczby całkowite. Fajny pomysł!
Martin Ender
1
@Martin: Cóż, to działa z GCC, ale tak naprawdę nie jestem pewien, czy to bona fide.
Ell
Chodzi mi o to, w jaki sposób można odróżnić wejść a = 1, b = 23i a = 12, b = 3. Czy 123w obu przypadkach nie musiałbyś używać STDIN?
Martin Ender
1
Jak powiedziałem, wydaje się działać (z wejściami 1 23i 12 3jako).
Ell
2
Ohhh, uwzględniacie spacje na wejściu. Tak, nie jestem zaskoczony, że to działa.
Martin Ender
5

64 61 znaków

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

Wyświetla wartości znaków -1, 0 i 1 odpowiednio dla wartości mniejszej, równej lub większej niż.

Ta implementacja opiera się na nieokreślonym zachowaniu dla bbycia typu inti dla danych wejściowych spoza zakresu INT_MIN / 2do INT_MAX / 2. Na platformach, na których podpisuje się przepełnienie, niezależnie od tego, czy uzupełnienie 2s (w zasadzie wszystkie z nich), czy wielkość znaku, zawiedzie w przypadku 25% możliwych par prawidłowych int. Co ciekawe (w każdym razie dla mnie), będzie działać poprawnie na platformach, na których nasycony jest przepełniony podpis.

laindir
źródło
To nie zadziała, jeśli się a-bprzepełni.
Dennis,
Niestety to prawda, ale nie mogłem wymyślić żadnego agnostycznego sposobu na platformę, aby tego uniknąć bez operatorów porównania. Pytanie nie określa zakresu danych wejściowych, dla których wyniki muszą być prawidłowe. Ta odpowiedź jest gwarantowana przez standard dla wszystkich danych wejściowych pomiędzy -(2^14)i 2^14 - 1na wszystkich platformach zgodnych i prawdopodobnie będzie działać dla znacznie większego zakresu na większości platform. Wszystkie pozostałe odpowiedzi w tym momencie przyjmują założenia dotyczące wielkości typu, względnych rozmiarów typów lub reprezentacji.
laindir
Pytanie zawiera dwie liczby całkowite ze znakiem jako dane wejściowe , więc powiedziałbym, że musi działać dla wszystkich par. main(a,b)jest już niezdefiniowanym zachowaniem, więc żadna z odpowiedzi nie będzie działać. Przenośność nie ma znaczenia.
Dennis
Masz absolutną rację, jeśli chodzi o niezdefiniowane zachowanie, więc moja implementacja naprawdę nie gwarantuje niczego według standardu. Dodam notatkę wskazującą jej ograniczenia.
laindir
4

 59    54 znaków

54 znaków z kompilatorem takim jak gcc, który nie dręczy main(x,y):

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

59 znaków inaczej:

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

Wynik:

  • Kod ASCII 0x00, jeśli x <y
  • Kod ASCII 0xFF, jeśli x> y
  • Kod ASCII 0x01, jeśli x == y
Todd Lehman
źródło
1
Zapewniam cię, main(x,y)działa w gcc, więc możesz upuścić te 5 bajtów z liczby postaci.
Martin Ender
main (x, y) nie działa na moim gcc. Być może wymagana jest opcja kompilatora. Ale możesz zastąpić main (x, y) x; main (y).
Florian F
3

66 102 bajtów

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

Odczytuje liczby całkowite ze STDIN i drukuje 0(a <b), 1(a> b) lub 2(a == b).

Edycja: Teraz powinno także działać w przypadku różnic, które są zbyt duże, aby zmieściły się w 32-bitowej liczbie całkowitej. Jestem pewien, że zagnieżdżone trójskładniki można skrócić za pomocą nieco więcej magii.

Martin Ender
źródło
Popraw mnie, jeśli się mylę, ale widzę <0 w twoim wewnętrznym trójcy.
overactor
@overactor naprawiono
Martin Ender
3

52 bajty

Niestety ten działa tylko dla dodatnich liczb całkowitych, ale pomyślałem, że koncepcja użycia operatorów czysto arytmetycznych była interesująca:

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

Wyjścia:

  • Kod ASCII 0xFF: mniej niż b
  • Kod ASCII 0x00: równa się b
  • Kod ASCII 0x01: większy niż b
Cyfrowa trauma
źródło
Jeśli wybierasz tylko dodatnie liczby całkowite, putchar(a/b-b/a)jest o wiele krótszy.
Dennis
@Dennis, który produkuje różne dane wyjściowe, na przykład dla (50,1) i (51,1). Ale udało mi się trochę skrócić.
Digital Trauma
1
Tak, nie myślałem poprawnie ...
Dennis
2

66 bajtów

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

Drukuje bajt 0x00 if a == b, 0x01 if a < bi 0xff if a > b.

Ponieważ znak ASCII inny niż ASCII lub niedrukowalny w programie [my] i jeśli coś nie jest wyraźnie zabronione w pytaniu, jest to dozwolone , znak niezadrukowany na wydruku powinien być całkowicie w porządku.

Dennis
źródło
Moja poprzednia wersja nie radziła sobie z przepełnieniem zbyt dobrze. Działa to na systemie Linux x64, gdzie longjest 64-bitowy.
Dennis
2

87 znaków

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

Używanie sztuczki 2 ^ 31 do konwersji na niepodpisane int

Przesyłanie podziału do niepodpisanego, aby obsługiwać górny bit jako dane, a nie podpisywać

Użycie ^ do XOR a i b, gdy są równe, zwraca 0

Używanie zagnieżdżonych instrukcji warunkowych (?) W celu uzyskania „<”, „>” lub „=” w celu przesłania do puts ()

Pelle
źródło
1

71 bajtów

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.
źródło
Twój ideone ma nawiasy z=x-yi jestem pewien, że są one konieczne. Możesz także zapisać dwa znaki, używając 49, 50` i 51bezpośrednio, zamiast dodawać 48.
Martin Ender
Przypisanie ma niższy priorytet niż operator trójskładnikowy: en.cppreference.com/w/c/language/operator_precedence
Martin Ender
W powyższym kodzie brakuje średnika i zawodzi -2000000000 2000000000, podobnie jak w przypadku innych kombinacji liczb całkowitych, które powodują przepełnienie odejmowania.
COTO,
1

68 znaków

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

Umieszcza znak ASCII 1, 2 lub 3 odpowiednio na wartość mniejszą, większą lub równą.

CompuChip
źródło
1
To nie zadziała, jeśli się a-bprzepełni.
Dennis,
1

88 89 bajtów

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

Zaczyna się to od dodania 1<<31( INT_MIN) do a i b, tak że 0 odpowiada teraz INT_MIN. Następnie zapętla i dekrementuje a i b co każdą pętlę, aż albo wyniesie 0, a następnie drukuje 0, 1 lub 2 w zależności od tego, czy a, b lub oba mają wartość 0.

120 119 bajtów

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

Nie jest to najkrótsze rozwiązanie, ale może być nieco bardziej golfa od lepszego golfisty niż ja. (Lub tylko ludzie z większą wiedzą o C niż ja)

Chodzi o to, aby zamaskować każdy bit, zaczynając od lewego i sprawdzając nierówność. Reszta powinna się wyjaśnić. Ponieważ liczby ujemne zaczynają się od 1 bitu, najpierw odwracam pierwszy bit za pomocą a^=1<<31.

overactor
źródło
W tej chwili nie mogę przetestować moich rozwiązań, więc możesz wskazać błędy.
overactor
Pierwsze rozwiązanie ma kilka problemów: 1. Szczęśliwa ;)buźka powinna być smutną );buźką. 2. a&btylko sprawdza, czy ai bmają bitów wspólnych; trzeba &&.
Dennis,
@Dennis, masz rację, dzięki.
overactor
1

Myślę, że nawet nie zamierzam pisać krótkiego kodu. Spróbuję wykonać to porównanie w sposób przenośny zgodnie ze specyfikacją C99.

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

Operator modulo zachowuje znak, ale może również generować zero (w tym zero ujemne), więc upewniamy się, że mamy zarówno nieparzystą, jak i parzystą wartość do sprawdzenia (nawet nie wiedząc, czy używamy tych uzupełnień). Operacje arytmetyczne mogą się przepełniać, ale bitowe nie ulegną przepełnieniu, a upewniając się, że zarówno bity są ustawione, jak i wyczyszczone, unikamy przypadkowego przekształcenia naszej liczby w ujemne zero lub pułapkę. Fakt, że dwie operacje są do tego dziwne, nie powinien mieć znaczenia, ponieważ możliwa reprezentacja pułapki nie powoduje niezdefiniowanego zachowania, dopóki nie zostanie ustawiona w wartości. Wykonanie operacji z przełączonym bitem 0 gwarantuje, że otrzymamy dokładnie jedną resztę niezerową. Uzbrojeni w znajomość obu znaków możemy zdecydować, jak kontynuować porównanie.

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

Ta metoda może być jedną z niewielu, które pozwalają na wyodrębnienie znaku liczby całkowitej ujemnego zera. Rozwiązujemy ten problem, wyraźnie sprawdzając zero. Gdybyśmy naprawdę grali w golfa, moglibyśmy oczywiście pozwolić na porównanie dwóch zer, aby również wykonać odejmowanie.

Yann Vernier
źródło
1

C 80 znaków

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

Drukuje „<”, „>” lub „=”, tak jak powinno.

C 63 znaków

Nowe podejście:

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

Drukuje „1”, „2” lub „3”.

Florian F.
źródło
1

W 64 znakach bez stdio.h

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

wypisuje „>” jeśli a> b, „<” jeśli a <b, „=” jeśli a == b int przepełnienie jest UB. Tylko nie przepełnij.

// 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_
źródło