Ответ на задачку по Си
2020-08-03 12:14Исчерпывающий ответ на задачу сложить два числа дал
spamsink : https://vak.dreamwidth.org/659406.html?thread=5230798#cmt5230798
Вот код на Си++, решающий поставленную задачу, не вызывая неопределённого поведения и не прибегая к трюкам.
Вот по этой причине физики не любят Си и Си++. Вы даже два числа сложить не можете без фокусов, говорят они. :)
Заметим, что проблема undefined behavior в арифметике свойственна только Си/Си++. Во многих современных языках, таких как Java, Go или Rust, она отсутствует.
Вот код на Си++, решающий поставленную задачу, не вызывая неопределённого поведения и не прибегая к трюкам.
Как видите, вовсе не теорема Ферма. Not a rocket science, как говорят американцы. Последовательность простых логических рассуждений.int add(int a, int b)
{
int sum;
if (a >= 0) {
if (b >= 0) {
// Оба числа положительные - сместим на верхнюю границу.
sum = (a - INT_MAX) + b;
if (sum > 0)
throw std::overflow_error("Integer overflow");
sum += INT_MAX;
} else {
// Разные знаки, нет переполнения.
sum = a + b;
}
} else {
if (b >= 0) {
// Разные знаки, нет переполнения.
sum = a + b;
} else {
// Оба числа отрицательные - сместим на нижнюю границу.
sum = (a - INT_MIN) + b;
if (sum < 0)
throw std::overflow_error("Integer overflow"); sum += INT_MIN;
}
}
return sum;
}
Вот по этой причине физики не любят Си и Си++. Вы даже два числа сложить не можете без фокусов, говорят они. :)
Заметим, что проблема undefined behavior в арифметике свойственна только Си/Си++. Во многих современных языках, таких как Java, Go или Rust, она отсутствует.

no subject
Date: 2020-08-03 20:22 (UTC)Я б убавил повторений, а так да, красиво насчет шифта.
Я так понимаю, что (a - INT_MAX) + b > 0 эквивалентно (a+b) < 0. Что, конечно, упрощает эти все вещи.
no subject
Date: 2020-08-03 20:52 (UTC)Решение предполагает что INT_MAX ≤ |INT_MIN| ≤ INT_MAX+1 Есть ли это в стандарте?
no subject
Date: 2020-08-03 21:21 (UTC)Допустим a и b уже >= 0.
Условие переполнения a + b очевидное: a + b > INT_MAX.
Но в C его так в лоб записать нельзя, т.к. это переполнение возникнет в проверке.
Но можно написать if (a > INT_MAX - b) или if (b > INT_MAX - a), и переполнения не возникнет.
Тут можно было бы и остановиться. Но можно переписать в форме
if ((a - INT_MAX) + b > 0) или if ((b - INT_MAX) + a > 0).
И тоже не будет переполнения.
no subject
Date: 2020-08-03 21:27 (UTC)no subject
Date: 2020-08-03 21:31 (UTC)no subject
Date: 2020-08-03 21:34 (UTC)a+b > INT_MAX в си можно написать как a+b < 0.
no subject
Date: 2020-08-03 21:41 (UTC)-INT_MAX <= (a - INT_MAX) <= 0
Целое со значением -INT_MAX существует всегда.
Т.е. тут прибавляем к неотрицательному b или 0, или отрицательное. Переполнения не возникает.
В (a - INT_MIN) + b при a и b < 0:
0 <= (a - INT_MIN) < -INT_MIN - 1
Целое со значением -INT_MIN - 1 существует всегда.
Т.е. тут аналогично прибавляем к отрицательному b или 0, или положительное, и переполнения не возникает.
Т.е. нет, решение не требует симметрии или его отсутствия в диапазоне целых. Спасает то, что в самом начале проверки a и b такие: >= 0 и <0, т.е. что нет проверки a <= 0.
а = 0 в (a - INT_MIN) дало бы переполнение в коде дополнения до двух (и не дало бы в двух других представлениях целых со знаком).
no subject
Date: 2020-08-03 21:43 (UTC)no subject
Date: 2020-08-03 21:44 (UTC)no subject
Date: 2020-08-03 21:47 (UTC)Ах вон как. Ну тогда ладно, тогда шифт - единственное решение.
no subject
Date: 2020-08-03 21:51 (UTC)no subject
Date: 2020-08-03 22:08 (UTC)no subject
Date: 2020-08-03 22:25 (UTC)и это будет точно быстрее работать :о))
no subject
Date: 2020-08-03 22:33 (UTC)А так-то, можно изобрести чо угодно. И без VHDL.
no subject
Date: 2020-08-03 23:03 (UTC)no subject
Date: 2020-08-03 23:04 (UTC)no subject
Date: 2020-08-04 01:33 (UTC)no subject
Date: 2020-08-04 05:35 (UTC)int add(int a, int b) { if (a > 0 && INT_MAX - a < b || a < 0 && INT_MIN - a > b) throw overflow_error("arithmetic overflow"); return a + b; }no subject
Date: 2020-08-04 07:27 (UTC)no subject
Date: 2020-08-04 07:33 (UTC)int add(int a, int b) { if (a >= 0) { if (b >= 0) { // Оба числа положительные if (INT_MAX - a < b) throw std::overflow_error("Integer overflow"); } else { // Разные знаки, нет переполнения } } else { if (b >= 0) { // Разные знаки, нет переполнения } else { // Оба числа отрицательные if (INT_MIN - a > b) throw std::overflow_error("Integer overflow"); } } return a + b; }И его, кстати, можно дальше оптимизировать.
Например, так:
int add(int a, int b) { if (a >= 0) { if (INT_MAX - a < b) throw std::overflow_error("Integer overflow"); } else { if (INT_MIN - a > b) throw std::overflow_error("Integer overflow"); } return a + b; }no subject
Date: 2020-08-04 09:29 (UTC)no subject
Date: 2020-08-04 09:43 (UTC)можно конечно зафигачть что-нть вроде
size_t eflags;
__asm__ __volatile__(
"pushfq \n\t"
"pop %%rax\n\t"
"movq %%rax, %0\n\t"
:"=r"(eflags)
:
:"%rax"
);
но это будет и платформозависимо и компайлерзависимо, то есть вообще кошерно нихт, пардон май котеговский
нормальным решением было бы дёргание обёртки через железное прерывание и генерация ивента для исключения, но это слишком нечеловеческое по видимому, двуногие очень любят хаос и костыли
Re: int add(int a, int b)
Date: 2020-08-06 16:05 (UTC)А ещё, это же не единственная "проблема Си/Си++".
Я в последнее время открыл для себя санитайзеры, так там есть соответствующая возможность обнаружить такие вещи - "-fsanitize=signed-integer-overflow".
Санитайзеры
Date: 2020-08-06 20:05 (UTC)Re: память выделяется динамически
Date: 2020-08-06 20:11 (UTC)С динамикой - валгринд отлично справляется, там среди опций - всё, что угодно можно делать.
Другое дело, не везде этот инструмент можно запустить, если у вас там совсем эмбеддед.
Re: память выделяется динамически
Date: 2020-08-06 21:22 (UTC)Re: память выделяется динамически
Date: 2020-08-07 07:40 (UTC)"Нормальные проекты" сами поставляют для своих пользователей файлы подавления нежелательных сообщений (gstreamer, glib, ...).
А потом, зря вы юнит-тесты пишите что ли!?
Самый простой тест (типа, создали компонент) уже может дать возможность для создания файла подавления или нахождения самых банальных ошибок (либо убедиться, что их нет).
Понятное дело, что если все тесты запускать - то "туши свет", особенно если много фолс позитивз.
"Разделяй и властвуй!"