Порівняйте два цілих числа в C або C ++ без операторів порівняння


13

Створіть найкоротшу програму, яка бере два підписані цілі числа як вхідні дані (через stdin або як аргументи) і відображає 3 різних виходи залежно від того, чи є перше число (1) більше, ніж (2) менше, або (3) рівне другому число.

Ловля

Ви не можете використовувати будь-яке з наступного у своїй програмі:

  • Стандартні оператори порівняння: <, >, <=, >=, ==, !=.
  • Будь-який файл бібліотеки , крім conio, stdioабо iostream.
  • Будь-який символ, що не належить до ASCII, або не для друку ASCII.

Переможець

Перемагає програма з найменшою кількістю символів.


Я вважаю, що використання таких речей, як abs без включення файлу бібліотеки (оскільки компілятор це все одно знає), також не дозволено?
Мартін Ендер

1
@ MartinBüttner так, це було б правильним припущенням. :)
grove

5
Чому обмеження на C (++)? Якщо ви хочете, щоб відповіді були портативними, незважаючи на нерозбірливість основних типів С, тоді слід зазначити це. Якщо це довільне обмеження, то слід пам’ятати, що довільні обмеження однієї мови на цьому сайті непопулярні.
Пітер Тейлор

8
@PeterTaylor - це частина виклику. Це було б зовсім іншим кульовим поєдинком, якби питання було мовним-агностичним. Обмеження його C / C ++ змінює стратегії, які використовуються під час вирішення проблеми. Я визнаю необхідність відкритості питань для більшості мов для сприяння участі більшої кількості людей, але в цій конкретній проблемі обмеження C / C ++ та їх конкретних операторів та методів є невід'ємною частиною проблеми.
гай

1
@EvilTeach так; якщо щось не прямо заборонено у питанні, то це дозволено.
гай

Відповіді:


2

53 байти

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

Релевантний лише перший символ виводу. Три різні виходи:

  1. '-', якщо b> a
  2. '0', якщо a == b
  3. будь-який інший символ, якщо a> b

Він працює для повного діапазону вводу int на всіх платформах, де sizeof (long)> sizeof (int).

Редагувати: коштує один додатковий символ, щоб замість нього "3" однозначно надрукувати "3":

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

6

Можливо, мені щось не вистачає в правилах, але ...

81 байт

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

Виходить, 00якщо a > b, -10якщо a == bі -1-1якщо a < b.


Настільки поширений, як цей вид коду, C не гарантує, що він працює. long longможе бути більше 64 біт, intможе бути настільки великим, щоб ви могли переповнювати, результат правильного зміщення негативних значень визначається реалізацією. Практично всі відповіді, отримані С, мають подібні питання.
Yann Vernier

1
@YannVernier: Зрозумів. Я здогадуюсь, що 100% безпроблемне рішення було б монстром, оскільки єдине, що ми могли б зробити безпечно (тобто без зсуву чи переповнення) - це трохи обертання, і щоб зробити це безпечно, нам потрібно було б визначити тривалість операндів за допомогою sizeof.
COTO

6

90 байт

Якщо ми можемо використовувати stdio, чому б не використати його можливості форматування для порівняння?

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

Передбачає сумісне з кодуванням ASCII та малоконтрольність.

72 байти

Коефіцієнти округлюються до нуля, але праві зрушення (на практиці) "округлені вниз". Це мертвий подарунок.

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

65 79 байт

Ще одна відмінна властивість негативних чисел полягає в тому, що вони виробляють від'ємний модуль. Це взагалі не залежить від цілого представлення; він навіть працює на моєму тостері 8-бітового надлишку-127! О, і оскільки ми можемо використовувати conio, чому б не зберегти два байти putch? Тепер, якби мені вдалося знайти лише свою копію 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 : Обробляйте великі відмінності, якщо вважати, що long longвони ширші, ніж int.


Я впевнений, що вам потрібен роздільник між %ds у вашому, scanfщоб однозначно проаналізувати два цілі числа. Хороша ідея, хоча!
Мартін Ендер

1
@Martin: Ну, це працює з GCC, але я дійсно не впевнений, чи це добросовісно.
Ell

Що я маю на увазі, як ви розрізняєте вхідні дані a = 1, b = 23та a = 12, b = 3. Чи не потрібно було б надягати 123STDIN в будь-якому випадку?
Мартін Ендер

1
Як я вже говорив, це, здається, працює ( 1 23і 12 3як входи).
Ell

2
О, ви включаєте пробіли у вхід. Так, насправді я не здивований.
Мартін Ендер

5

64 61 символ

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

Виводить значення символів -1, 0 та 1 на менше, рівне або більше, ніж відповідно.

Ця реалізація покладається на невизначене поведінку bяк для типу, так intі для входів за межі діапазону INT_MIN / 2до INT_MAX / 2. На платформах, де підписано переповнення, чи 2-доповнення (в основному всі вони), чи величина знаку, він не матиме 25% можливих пар дійсних int. Цікаво (мені все одно), що він буде правильно працювати на платформах, де підписані переповнення насичують.


Це не спрацює, якщо a-bпереповнює.
Денніс

На жаль, це правда, але я не міг придумати жодного агностичного способу платформи, щоб уникнути цього без операторів порівняння. Питання не вказує діапазон входів, результати яких мають бути дійсними. Ця відповідь гарантується стандартом для роботи на всіх входах між -(2^14)і 2^14 - 1на всіх сумісних платформах, і, ймовірно, буде працювати для значно більшого діапазону на більшості платформ. Всі інші відповіді на даний момент роблять припущення щодо розміру типу, відносних розмірів типів або представлення.
laindir

У запитанні вказано два підписані цілі числа як вхідні дані , тому я б сказав, що він повинен працювати для всіх пар. main(a,b)це вже не визначена поведінка, тому жодна з відповідей не гарантовано спрацює. Не забудьте про портативність.
Денніс

Ви абсолютно праві, що стосується невизначеної поведінки, тому моє впровадження насправді нічого не гарантує. Додам примітку із зазначенням її обмежень.
laindir

4

 59    54 символи

54 діаграми з компілятором, наприклад, gcc, який не працює на main(x,y):

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

59 символів інакше:

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

Вихід:

  • ASCII код 0x00, якщо x <y
  • ASCII код 0xFF, якщо x> y
  • ASCII код 0x01, якщо x == y

1
Можу запевнити, main(x,y)працює в gcc, тому сміливо відкиньте ці 5 байт з вашого числа символів.
Мартін Ендер

main (x, y) не працює на моєму gcc. Можливо, потрібен варіант компілятора. Але ви можете замінити main (x, y) на x; ​​main (y).
Флоріан Ф

3

66 102 байти

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

Читає цілі числа зі STDIN і друкує 0(a <b), 1(a> b) або 2(a == b).

Редагувати: Тепер він також повинен працювати для відмінностей, які занадто великі, щоб вписатись у 32-бітове ціле число. Я впевнений, що вкладений трійник можна скоротити ще трохи біт-магією.


Виправте мене, якщо я помиляюся, але у вашому внутрішньому трійці я бачу <0.
овертайк

@overactor зафіксовано
Мартін Ендер

3

52 байти

На жаль, це працює лише для натуральних чисел, але я вважав, що концепція використання чисто арифметичних операторів є цікавою:

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

Виходи:

  • ASCII код 0xFF: a менше b
  • ASCII код 0x00: дорівнює b
  • ASCII код 0x01: більший за b

Якщо ви збираєтеся лише за додатними цілими числами, putchar(a/b-b/a)це набагато коротше.
Денніс

@ Денніс, який виробляє різні виходи, наприклад для (50,1) та (51,1). Але я зміг трохи скоротити.
Digital Trauma

1
Так, не думав належним чином ...
Денніс

2

66 байт

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

Друкує байт 0x00 if a == b, 0x01 if a < bта 0xff if a > b.

Оскільки символ ASCII, що не належить до друку, або недрукований символ ASCII в програмі [my] і якщо щось не прямо заборонено у питанні, то це дозволено , недрукований символ у вихідному документі повинен бути повністю чітким.


Моя попередня версія не справлялась із переповненням дуже добре. Це працює на x64 Linux, де long64-бітні.
Денніс

2

87 символів

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

Використання трюку 2 ^ 31 для перетворення в неподписані int

Лиття підрозділу без підпису, щоб обробляти верхній біт як дані, а не знак

Використовуючи ^ до XOR a і b, коли вони рівні, це повертає 0

Використання вкладених умовних умов (?) Для отримання "<", ">" або "=" для подачі на put ()


1

71 байт

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

http://ideone.com/uvXm6c


У вашого ідеона є круглі дужки, z=x-yі я впевнений, що вони необхідні. Ви також можете зберегти два символи, використовуючи 49, 50` та 51безпосередньо, замість того, щоб додавати 48.
Мартін Ендер

Призначення має нижчий пріоритет, ніж потрійний оператор: en.cppreference.com/w/c/language/operator_precedence
Мартін Ендер

У вашому коді, зазначеному вище, відсутня крапка з комою, і він не вдається -2000000000 2000000000, як і будь-яка інша комбінація цілих чисел, що спричиняють переповнення віднімання.
COTO

1

68 символів

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

Ставить символ ASCII 1, 2 або 3 на менше, більше або рівний відповідно.


1
Це не спрацює, якщо a-bпереповнює.
Денніс

1

88 89 байт

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

Це починається з додавання 1<<31( INT_MIN) до a і b, так що тепер відповідає 0 INT_MIN. Потім петлі та зменшення a і b кожен цикл, поки будь-який з них 0, потім друкує 0, 1 або 2 залежно від того, a, b або обидва є 0.

120 119 байт

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

Це не найкоротше рішення, як це є, але може бути гольф трохи кращим гравцем, ніж я. (Або просто люди з більш знанням C, ніж я)

Ідея полягає в маскуванні кожного шматочка, починаючи з лівого і перевіряючи на нерівність. Решта повинна пояснити себе. Оскільки від’ємні числа починаються з 1 біта, я спочатку інвертую перший біт a^=1<<31.


Я не можу перевірити свої рішення на даний момент, тому сміливо вказуйте на помилки.
овертайк

Перше рішення має кілька проблем: 1. Щасливий ;)смайлик повинен бути сумним );смайликом. 2. a&bтільки тести, якщо aі bмають спільні біти; вам потрібно &&.
Денніс

@Dennis, ти маєш рацію, дякую.
суперектор

1

Я думаю, я навіть не збираюся намагатися написати короткий код. Я спробую виконати це порівняння способом, який є портативним за специфікацією C99.

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

Оператор модуля зберігає знак, але він цілком може створити нуль (включаючи від’ємний нуль), тому ми гарантуємо, що ми перевіряємо як непарне, так і парне значення (навіть не знаючи, чи використовуємо вони доповнення). Арифметичні операції можуть переповнюватись, але порозрядно не буде, і, гарантуючи, що є біти, як встановлені, так і очищені, ми уникаємо ненавмисного перетворення нашого числа у від’ємний нуль або значення пастки. Факт, що для цього потрібно зробити дві дивні дії, не має значення, оскільки можливе подання пастки не викликає невизначеної поведінки, поки не буде поставлено значення. Виконуючи операцію з переключеним бітом 0, ми отримуємо рівно один ненульовий залишок. Озброївшись знаннями обох ознак, ми можемо вирішити, як діяти зі порівнянням.

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

Цей спосіб може бути одним з небагатьох, що дозволяє витягувати знак цілого від’ємного нуля. Ми вирішуємо це, чітко перевіряючи нуль. Якби ми займалися гольфом по-справжньому, ми могли б, звичайно, дозволити порівняння двох нулів, щоб також виконати віднімання.


1

C 80 символів

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

Він друкує '<', '>' або '=', як слід.

C 63 символів

Новий підхід:

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

Друкує '1', '2' або '3'.


1

У 64 символах без stdio.h

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

друкує '>', якщо a> b, '<' якщо a <b, '=' якщо a == b int overflow є UB. Просто не переливайтеся.

// 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) ? ">" : "=");
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.