vak: (Default)
[personal profile] vak
Сложить два целых числа, вернуть целое. Породить исключение в случае арифметического переполнения.
int add(int a, int b);
Удивительно, сколько народу обламывается на этой задачке.

Date: 2020-08-02 11:49 (UTC)
From: [personal profile] borisk
То есть, заглянуть вот сюда, к примеру:

https://en.cppreference.com/w/cpp/language/try_catch

— выше сил аппликантов?

Date: 2020-08-02 12:19 (UTC)
mopexod: (Default)
From: [personal profile] mopexod
Целочисленная арифметика исключений не вызывает.

Date: 2020-08-02 17:51 (UTC)
From: [personal profile] borisk
Вот поэтому я и не программист.

Date: 2020-08-02 13:00 (UTC)
From: [personal profile] caztd
Типичный пример задачи, которую вредно решать самому.
Короткий гугл-поиск дает ответ (только gcc конечно):

int add(int lhs, int rhs) {
    int sum;
    if (__builtin_add_overflow(lhs, rhs, &sum))
        abort(); // or throw;
    return sum;
}

Date: 2020-08-02 13:08 (UTC)
vit_r: default (Default)
From: [personal profile] vit_r
Вот потому и не могут решить. Copy-paste даёт ответ, но не объясняет, как его получить.

А когда вставляют в код ответ не от той задачи, совсем весело.

Date: 2020-08-02 13:13 (UTC)
From: [personal profile] caztd
Как получить ответ -- это вопрос на экзамене а не для приема на работу.
Первая истина для разработчика -- не умножай сущностей,
то есть не выдумывай заново, то что уже придумано до тебя.
Если человек не нашел builtin'a в гугле, то он уже не квалифицирован для разработки.
И знает ли он о том что signed integer overflow это UB или не знает это уже совершенно неважно.

(no subject)

From: [personal profile] vit_r - Date: 2020-08-02 13:16 (UTC) - Expand

(no subject)

From: [personal profile] caztd - Date: 2020-08-02 13:21 (UTC) - Expand

(no subject)

From: [personal profile] vit_r - Date: 2020-08-02 13:25 (UTC) - Expand

Date: 2020-08-02 13:13 (UTC)
vit_r: default (Default)
From: [personal profile] vit_r
Задача неоднозначно поставлена. Можно преобразовать в длинные, сложить длинные и короткие, а потом сравнить результаты. Что будет правильным ответом, потому что про производительность никто не спрашивал.

Date: 2020-08-02 15:08 (UTC)
juan_gandhi: (Default)
From: [personal profile] juan_gandhi

Довольно логично, и не так уж неэффективно.

Date: 2020-08-02 16:17 (UTC)
vit_r: default (Default)
From: [personal profile] vit_r
Если нам нужна действительно базовая операция типа сложения, там надо оптимизировать до тактов, учитывая разрядность шины в байтах. То есть, или получать доступ к специальным функциям определённой архитектуры процессора, или делать какой-нибудь финт ушами типа проверки на смену знака, сравнения с разностью, проверки на особые случаи байтовыми операциями.

Короче, настоящий программист ломанётся в дебри и обязательно запутается, потому что не успеет продумать все ньюансы.

(no subject)

From: [personal profile] juan_gandhi - Date: 2020-08-02 16:38 (UTC) - Expand

Date: 2020-08-02 18:59 (UTC)
dluciv: (Default)
From: [personal profile] dluciv
Более того, глядя на оптимизации современных компиляторов, я не исключаю, что с -O3 компилятор, если его специально не пугать, оставит в итоге только то, что надо.

(no subject)

From: [personal profile] vit_r - Date: 2020-08-02 19:22 (UTC) - Expand

(no subject)

From: [personal profile] archaicos - Date: 2020-08-02 22:27 (UTC) - Expand

Date: 2020-08-02 18:37 (UTC)
archaicos: Шарж (Default)
From: [personal profile] archaicos
Есть машинки (типа UNISYS Clearpath), у которых только тип char маленький (8 бит), а все остальные целочисленные (в т.ч. short и long) одинаково большие (96 бит).
Т.е. там более длинных целочисленных типов просто нет.

(no subject)

From: [personal profile] vit_r - Date: 2020-08-02 19:19 (UTC) - Expand

(no subject)

From: [personal profile] archaicos - Date: 2020-08-02 20:33 (UTC) - Expand

Date: 2020-08-02 13:29 (UTC)
waqur: (Default)
From: [personal profile] waqur
Ну, задачка не такая уж и простая. :)
Для беззнаковых типичный паттерн проверки на переполнение — это сложить и проверить, что результат сложения не меньше одного из операндов (любого). А для знаковых — не знаю, ветвиться по знаковости операндов нужно, наверное, на все четыре случая. А в ветках - сводить всё к беззнаковым и применять тот же приёмчик. Это в предположении, что просто перейти к типам большей размерности нельзя, например, такая платформа, что sizeof(int) == sizeof(intmax_t).

Date: 2020-08-02 13:45 (UTC)
waqur: (Default)
From: [personal profile] waqur
Вспомнил про флаг OF в интеловской архитектуре x86 и полез в Википедию читать правила его вычисления:

Internally, the overflow flag is usually generated by an exclusive or of the internal carry into and out of the sign bit.

Мило, но вряд-ли аппликант на работу додумается до этого на собеседовании, если не знает заранее.

Carry out of the sign bit можно вычислить, как я уже говорил, после кастинга обоих операндов к unsigned int, их сложения, и сравнения суммы с любым из них, например первым. Если сумма беззнаковых меньше первого беззнакового — значит бит Carry out равен 1, иначе 0.

Carry in можно вычислить, просто применив AND-маску ((unsigned int)(INT_MAX)) к обоим операндам в беззнаковой форме перед сложением. Нужный нам бит "Carry in of the sign bit" будет в старшем бите результата после сложения.

А далее просто XOR этих carry in и carry out, как учит Википедия. :)

Интересно, найдётся ли компилятор, который преобразует все эти извращения обратно в инструкцию чтения флага OF.

Date: 2020-08-02 15:09 (UTC)
juan_gandhi: (Default)
From: [personal profile] juan_gandhi

Достаточно проверить равенство знаков, и пробовать сложить абсолютные величины.

(no subject)

From: [personal profile] waqur - Date: 2020-08-02 15:48 (UTC) - Expand

(no subject)

From: [personal profile] juan_gandhi - Date: 2020-08-02 15:55 (UTC) - Expand

(no subject)

From: [personal profile] archaicos - Date: 2020-08-02 18:48 (UTC) - Expand

(no subject)

From: [personal profile] waqur - Date: 2020-08-02 22:38 (UTC) - Expand

(no subject)

From: [personal profile] archaicos - Date: 2020-08-02 23:10 (UTC) - Expand

(no subject)

From: [personal profile] waqur - Date: 2020-08-03 08:08 (UTC) - Expand

(no subject)

From: [personal profile] archaicos - Date: 2020-08-03 15:50 (UTC) - Expand

(no subject)

From: [personal profile] lomeo - Date: 2020-08-03 07:53 (UTC) - Expand

Date: 2020-08-02 14:22 (UTC)
sobriquet9: (Default)
From: [personal profile] sobriquet9
Это точно про C++, а не про представление целых чисел? Я думаю, что вот такое будет работать, но не уверена, что оно соответствует стандарту (фиг знает какие ещё могут быть внутренние представления и когда в них выскочит Undefined Behavior)

int add(int a, int b)
{
  int sum = a + b;
  if (a > 0 && b > 0 && sum < -1) {
    throw std::overflow_error("result too positive");
  } else if (a < 0 && b < 0 && sum > -1) {
    throw std::overflow_error("result too negative");
  }
  return sum;
}

Date: 2020-08-02 18:38 (UTC)
archaicos: Шарж (Default)
From: [personal profile] archaicos
UB возникает во время +. Потом на результат от этого + смотреть уже поздно.

(no subject)

From: [personal profile] waqur - Date: 2020-08-02 19:34 (UTC) - Expand

(no subject)

From: [personal profile] archaicos - Date: 2020-08-02 20:30 (UTC) - Expand

(no subject)

From: [personal profile] waqur - Date: 2020-08-02 22:13 (UTC) - Expand

(no subject)

From: [personal profile] archaicos - Date: 2020-08-02 22:25 (UTC) - Expand

(no subject)

From: [personal profile] waqur - Date: 2020-08-02 22:34 (UTC) - Expand

(no subject)

From: [personal profile] archaicos - Date: 2020-08-02 22:58 (UTC) - Expand

(no subject)

From: [personal profile] waqur - Date: 2020-08-03 08:20 (UTC) - Expand

(no subject)

From: [personal profile] sobriquet9 - Date: 2020-08-02 20:25 (UTC) - Expand

(no subject)

From: [personal profile] archaicos - Date: 2020-08-02 20:44 (UTC) - Expand

(no subject)

From: [personal profile] sobriquet9 - Date: 2020-08-02 22:49 (UTC) - Expand

(no subject)

From: [personal profile] archaicos - Date: 2020-08-02 23:15 (UTC) - Expand

Date: 2020-08-02 18:07 (UTC)
ircicq: (Default)
From: [personal profile] ircicq
int add(int a, int b) {
    int sum = a + b;
    if (~(a ^ b) & (a ^ sum) & INT_MIN)
         throw overflow_error("arithmetic overflow");
    return sum;
}

(no subject)

From: [personal profile] ircicq - Date: 2020-08-02 18:38 (UTC) - Expand

(no subject)

From: [personal profile] ex0_planet - Date: 2020-08-02 21:05 (UTC) - Expand

Date: 2020-08-02 21:08 (UTC)
From: [personal profile] ex0_planet
С вычитанием прикольнее.

Но вообще задачка проверяет не столько знание Си, сколько умение искать информацию :-)

Date: 2020-08-02 22:31 (UTC)
archaicos: Шарж (Default)
From: [personal profile] archaicos
Большинство тупо не знает что искать. Им говоришь, что есть проблема в "a+b", или спрашиваешь, нет ли в тут проблемы. И они глазами хлоп-хлоп. Искать можно уже только к следующему собеседованию.

(no subject)

From: [personal profile] ex0_planet - Date: 2020-08-03 13:23 (UTC) - Expand

Date: 2020-08-03 08:43 (UTC)
spamsink: (Default)
From: [personal profile] spamsink
Кто все эти безумные люди, которые рассуждают про unsigned, какие-то там детали реализации каких-то там конкретных версий языков программирования, про битовые операции, 2's complement представление? Какая нахер разница? Машина может быть хоть троичная симметричная, и не иметь понятия об "unsigned".

Пятикласснику должно быть понятно, что достаточно знать только MAX и MIN для данного типа операндов, и делается это так: нарочно не на языке программирования, а на естественном языке.

1. Если хотя бы одно из чисел равно нулю или числа с разным знаком, то складываем без опаски.
2. Если оба числа положительные, то вычтем из MAX одно из них. Если разность меньше другого числа, то бросаем исключение.
3. Если оба числа отрицательные, то вычтем из MIN одно из них. Если разность больше другого числа, то бросаем исключение.
4. Складываем числа, возвращаем результат.

По-хорошему, спрашивать надо про
template< typename T > int add(const T & a, const T & b); и вопрос тогда будет про type traits (который синтаксис я так и не запомнил).

Date: 2020-08-03 23:30 (UTC)
From: [personal profile] caztd
Ну если уж на то пошло, то машина может и не иметь концепта арифметического переполнения или MAX/MIN для определенного типа операндов. Поэтому совсем от машины абстрагироваться не получится.

А уж если мы говорим про язык Си, то мы обычно говорим об этом не для красоты, а для того чтоб писать эффективный и быстрый код. Потому что если вам не нужен такой код, то зачем вам Си? Берите Питон или JS какой-нибудь в самом деле. Ну а если вам нужен эффективный и быстрый код, то вам никогда-никогда не следует использовать вышеуказанный алгоритм для пятиклассников. Потому что он медленный, плохо-читается,
в нем легко сделать ошибку, которую потом будет трудно найти.

Но спасибо за такой вариант. Я еще раз убедился, что даже в мире программистов на Си есть миры, которые не просто слегка отличаются, а не соприкасаются друг с другом вообще. Какая уж там политика может быть, когда люди не просто разные бывают, а многие прямо-таки абсолютно чужды друг другу.

(no subject)

From: [personal profile] spamsink - Date: 2020-08-04 05:03 (UTC) - Expand