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


12

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

Поймать

Вы не можете использовать в своей программе следующее:

  • Стандартные операторы сравнения: <, >, <=, >=, ==, !=.
  • Любой файл библиотеки , кроме conio, stdioили iostream.
  • Любой не-ASCII или непечатный ASCII-символ.

Победитель

Программа с самым коротким количеством символов выигрывает.


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

1
@ MartinBüttner да, это было бы правильным предположением. :)
Роща

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 печатать '+':

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может быть настолько большим, чтобы вы могли переполниться, результат смещения вправо отрицательных значений определяется реализацией. Практически все C-производные ответы имеют похожие проблемы.
Ян Вернье,

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

РЕДАКТИРОВАТЬ : обрабатывать большие различия, предполагая, 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. На платформах, где происходит переполнение со знаком, будь то дополнение 2s (в основном все) или величина знака, оно завершится неудачно для 25% возможных пар допустимых значений int. Интересно (во всяком случае, для меня), что он будет работать правильно на платформах, где насыщение со знаком насыщается.


Это не будет работать, если a-bпереполнится.
Деннис

К сожалению, это правда, но я не мог придумать какого-либо независимого от платформы способа избежать этого без операторов сравнения. Вопрос не определяет диапазон входных данных, для которых результаты должны быть действительными. Этот ответ гарантирован стандартом для всех входов между -(2^14)и 2^14 - 1на всех совместимых платформах, и он, вероятно, будет работать для значительно большего диапазона на большинстве платформ. Все остальные ответы на этом этапе делают предположения о размере типа, относительных размерах типов или представлении.
laindir

Вопрос говорит, что два входных целых числа в качестве входных данных , так что я бы сказал, что это должно работать для всех пар. main(a,b)это уже неопределенное поведение, поэтому ни один из ответов не гарантированно сработает. Фигу портативность.
Деннис

Вы абсолютно правы в отношении неопределенного поведения, поэтому моя реализация на самом деле ничего не гарантирует по стандарту. Я добавлю примечание, указывающее, каковы его ограничения.
laindir

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 в вашей внутренней тройной.
'11

Исправлен @overactor
Мартин Эндер

3

52 байта

К сожалению, этот работает только для натуральных чисел, но я думал, что концепция использования чисто арифметических операторов была интересна:

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

Выходы:

  • ASCII код 0xFF: меньше чем b
  • Код ASCII 0x00: равен b
  • Код ASCII 0x01: больше, чем b

Если вы идете только для натуральных чисел, putchar(a/b-b/a)это намного короче.
Деннис

@Dennis, который производит различные выходные данные, например, для (50,1) и (51,1). Но я смог немного сократить, хотя.
Цифровая травма

1
Да, не думал должным образом ...
Деннис

3

 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).
Florian F

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.

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


Моя предыдущая версия не очень хорошо справлялась с переполнением. Это работает на 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 для преобразования в unsigned int

Приведение деления к unsigned для обработки старшего бита как данных, а не знака

Используя ^ к 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.


Пока я не могу проверить свои решения, поэтому не стесняйтесь указывать на ошибки.
'35

Первое решение имеет пару проблем: 1. Счастливый ;)смайлик должен быть грустным );смайликом. 2. a&bтолько проверяет, если aи bимеют общие биты; вам нужно &&.
Деннис

@ Денис, ты прав, спасибо.
Сверхактор

1

Я думаю, что даже не собираюсь пытаться писать короткий код. Я попытаюсь выполнить это сравнение так, чтобы оно было переносимым в соответствии со спецификацией C99.

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

Оператор по модулю сохраняет знак, но он вполне может произвести ноль (включая отрицательный ноль), поэтому мы гарантируем, что у нас есть как нечетное, так и четное значение для его проверки (даже не зная, используем ли мы их дополнение). Арифметические операции могут переполняться, но побитовые не будут, и, гарантируя, что биты установлены и очищены, мы избегаем непреднамеренного преобразования нашего числа в отрицательный ноль или значение ловушки. Тот факт, что две операции требуются для этого странным образом, не должен иметь значения, потому что возможное представление ловушек не вызывает неопределенного поведения, пока не будет помещено в lvalue. Выполнение операции с переключенным битом 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 переполнение равно 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.