Я писал программу на C ++, чтобы найти все решения a b = c , где a , b и c вместе используют все цифры 0-9 ровно один раз. Программа зациклилась на значениях a и b и каждый раз запускала процедуру подсчета цифр для a , b и a b, чтобы проверить, было ли выполнено условие цифр.
Тем не менее, ложные решения могут быть получены при б перетекает предел целого. В итоге я проверил это, используя такой код:
unsigned long b, c, c_test;
...
c_test=c*b; // Possible overflow
if (c_test/b != c) {/* There has been an overflow*/}
else c=c_test; // No overflow
Есть ли лучший способ тестирования на переполнение? Я знаю, что у некоторых микросхем есть внутренний флаг, который устанавливается при переполнении, но я никогда не видел, чтобы к нему обращались через C или C ++.
Имейте ввиду , что подписал int
переполнении неопределенное поведение в C и C ++ , и , таким образом , вы должны обнаружить его фактически не вызывает его. Информацию о переполнении со знаком int перед добавлением см. В разделе « Обнаружение переполнения со знаком в C / C ++» .
источник
-ftrapv
заставит его генерировать SIGABRT при переполнении (со знаком) целого числа. Смотрите здесь .clz
инструкцию или__clz(unsigned)
функцию, чтобы определить ранг числа (где его старший бит). Так как я не уверен, доступно ли это на x86 или x64, я предположу, что это не так, и скажу, что нахождение наиболее значимого бита потребует худшихlog(sizeof(int)*8)
инструкций.Ответы:
Я вижу, вы используете целые числа без знака. По определению, в C (не знаю о C ++), неподписанные арифметика не переполнения ... так, по крайней мере , для C, ваша точка является спорным :)
В случае целых чисел со знаком, после переполнения происходит неопределенное поведение (UB), и ваша программа может делать все что угодно (например, сделать тесты неокончательными).
Чтобы создать соответствующую программу, вам нужно проверить на переполнение, прежде чем генерировать указанное переполнение. Метод также можно использовать с целыми числами без знака:
Для деления (за исключением
INT_MIN
и-1
особого случая) нет никакой возможности перейтиINT_MIN
илиINT_MAX
.источник
x >= 0
-x > 0
будет достаточным (еслиx == 0
, тогдаx + a
не может переполниться по очевидным причинам).if ((a < INT_MIN / x))
тест слишком поздноif (x == -1)
Тест необходим первый.Там является способ определить , является ли операция, вероятно, переполнение, используя позиции наиболее значимых одного бита в операндах и немного основной двоично-математические знания.
Кроме того, любые два операнда будут приводить (не более) на один бит больше, чем старший бит самого большого операнда. Например:
Для умножения любые два операнда приведут (самое большее) к сумме битов операндов. Например:
Точно так же вы можете оценить максимальный размер результата
a
до степени,b
подобной этой:(Конечно, подставьте число бит для целевого целого числа.)
Я не уверен в самом быстром способе определения позиции старшего однобитного числа, вот метод грубой силы:
Это не идеально, но это даст вам хорошее представление о том, могут ли переполниться любые два числа перед выполнением операции. Я не знаю, будет ли это быстрее, чем просто проверка результата, как вы предложили, из-за цикла в
highestOneBitPosition
функции, но это может произойти (особенно если вы знали, сколько битов было в операндах заранее).источник
log2
, но это не обязательно будет так очевидно для человека, у которого нет математического образования.multiplication_is_safe
0x8000 * 0x10000
переполнением (битовые позиции 16 + 17 = 33, что > 32 ), хотя это не так, потому0x8000 * 0x10000 = 0x80000000
что, очевидно, все еще вписывается в 32-битное целое число без знака. Это только один из многих примеров, для которых этот код не работает.0x8000 * 0x10001
, ...0x8000 * 0x10000
не "безопасно" по этому определению, даже если все в порядке.Clang 3.4+ и GCC 5+ предлагают проверенные арифметические встроенные функции. Они предлагают очень быстрое решение этой проблемы, особенно по сравнению с проверками безопасности при битовом тестировании.
Для примера в вопросе OP это будет работать так:
Документация Clang не указывает,
c_test
содержит ли переполненный результат, если произошло переполнение, но в документации GCC говорится, что это так. Учитывая, что эти двое любят быть__builtin
-совместимыми, вероятно, можно с уверенностью предположить, что именно так работает Clang.Для
__builtin
каждой арифметической операции существует возможность переполнения (сложение, вычитание, умножение) с вариантами со знаком и без знака, для размеров int, long и long long. Синтаксис имени__builtin_[us](operation)(l?l?)_overflow
:u
для неподписанных илиs
для подписанных ;add
,sub
илиmul
;l
суффикса означает, что операндыint
s; одноl
средствоlong
; дваl
с видуlong long
.Так что для проверенного подписанного длинного целого сложения это было бы
__builtin_saddl_overflow
. Полный список можно найти на странице документации Clang .GCC 5+ и Clang 3.8+ дополнительно предлагают общие внутренние команды , которые работают без указания типа значений:
__builtin_add_overflow
,__builtin_sub_overflow
и__builtin_mul_overflow
. Они также работают на типах меньше, чемint
.Встроенные опускаются до того, что лучше для платформы. На x86 они проверяют флаги переноса, переполнения и подписи.
Файл cl.exe в Visual Studio не имеет прямых эквивалентов. Для беззнаковых сложений и вычитаний, включая
<intrin.h>
, позволит вам использоватьaddcarry_uNN
иsubborrow_uNN
(где NN - количество бит, напримерaddcarry_u8
илиsubborrow_u64
). Их подпись немного тупая:c_in
/b_in
- флаг переноса / заимствования на входе, а возвращаемое значение - перенос / заимствование на выходе. Похоже, он не имеет эквивалентов для подписанных операций или умножений.В противном случае Clang для Windows теперь готов к работе (достаточно для Chrome), так что это тоже может быть вариантом.
источник
__builtin_sub_overflow
определенно не в Clang 3.4.__builtin_add_overflow
и друзья уже должны быть доступны на Clang 3.8.Некоторые компиляторы предоставляют доступ к целочисленному флагу переполнения в ЦП, который вы затем можете проверить, но это не является стандартным.
Вы также можете проверить возможность переполнения перед выполнением умножения:
источник
b == ULONG_MAX / a
? Тогда это все еще может соответствовать, учитывая, чтоa
делитULONG_MAX
без остатка.Предупреждение: GCC может оптимизировать проверку переполнения при компиляции с
-O2
. Опция-Wall
выдаст вам предупреждение в некоторых случаях, таких какно не в этом примере:
Единственный безопасный способ - проверить переполнение до того, как оно произойдет, как описано в документе CERT , и систематически использовать его было бы невероятно утомительно.
Компиляция с
-fwrapv
решает проблему, но отключает некоторые оптимизации.Нам отчаянно нужно лучшее решение. Я думаю, что компилятор должен выдавать предупреждение по умолчанию при выполнении оптимизации, которая полагается на переполнение, которое не происходит. Нынешняя ситуация позволяет компилятору оптимизировать проверку переполнения, что, на мой взгляд, неприемлемо.
источник
for(int k = 0; k < 5; k++) {...}
должно поднять предупреждение?k
могут быть легко определены во время компиляции. Компилятор не должен делать никаких предположений.n
он меньше 32, прежде чем выдавать команду сдвига, которая использует только младшие 5 битn
?Clang теперь поддерживает динамические проверки переполнения для целых чисел со знаком и без знака. Смотрите ключ -fsanitize = integer . На данный момент это единственный компилятор C ++ с полностью поддерживаемой динамической проверкой переполнения для целей отладки.
источник
Я вижу, что многие люди ответили на вопрос о переполнении, но я хотел решить его первоначальную проблему. Он сказал, что проблема в том, чтобы найти б = c, чтобы все цифры использовались без повторения. Хорошо, это не то, что он спросил в этом посте, но я все еще думаю, что было необходимо изучить верхнюю границу проблемы и сделать вывод, что ему никогда не понадобится рассчитывать или обнаруживать переполнение (примечание: я не опытный в математике, поэтому я сделал это шаг за шагом, но конечный результат был настолько прост, что это может иметь простую формулу).
Суть в том, что верхняя граница, которая требуется для задачи a, b или c, равна 98.765.432. В любом случае, начнем с разбиения задачи на тривиальные и нетривиальные части:
Теперь нам просто нужно показать, что никакое другое решение невозможно и допустимы только перестановки (и тогда код для их печати тривиален). Возвращаемся к верхней границе. На самом деле верхняя граница составляет c ≤ 98,765,432. Это верхняя граница, потому что это наибольшее число с 8 цифрами (всего 10 цифр минус 1 для каждого a и b). Эта верхняя граница только для c, потому что границы для a и b должны быть намного ниже из-за экспоненциального роста, как мы можем вычислить, варьируя b от 2 до верхней границы:
Обратите внимание, например, на последнюю строку: там написано, что 1.97 ^ 27 ~ 98M. Так, например, 1 ^ 27 == 1 и 2 ^ 27 == 134.217.728, и это не решение, потому что оно имеет 9 цифр (2> 1,97, поэтому оно на самом деле больше, чем должно быть проверено). Как видно, комбинации, доступные для тестирования a и b, действительно малы. Для b == 14 нам нужно попробовать 2 и 3. Для b == 3 мы начинаем с 2 и останавливаемся на 462. Все результаты предоставляются как минимум ~ 98M.
Теперь просто протестируйте все комбинации выше и найдите те, которые не повторяют никаких цифр:
Ни один из них не соответствует проблеме (что также видно по отсутствию '0', '1', ..., '9').
Пример кода, который решает это следующим образом. Также обратите внимание, что это написано на Python не потому, что ему нужны произвольные целые числа точности (код не рассчитывает ничего больше, чем 98 миллионов), а потому, что мы обнаружили, что количество тестов настолько мало, что мы должны использовать язык высокого уровня для использовать его встроенные контейнеры и библиотеки (также обратите внимание: код имеет 28 строк).
источник
Вот «непереносимое» решение вопроса. Процессоры Intel x86 и x64 имеют так называемый EFLAGS-регистр , который заполняется процессором после каждой целочисленной арифметической операции. Я пропущу подробное описание здесь. Соответствующими флагами являются флаг «Переполнение» (маска 0x800) и флаг «Перенос» (маска 0x1). Чтобы правильно их интерпретировать, следует учитывать, имеют ли операнды тип со знаком или без знака.
Вот практический способ проверить флаги из C / C ++. Следующий код будет работать в Visual Studio 2005 или более поздней версии (как 32-разрядной, так и 64-разрядной), а также 64-разрядной версии GNU C / C ++.
Если бы операнды были умножены без переполнения, вы получите возвращаемое значение 0
query_intel_eflags(0x801)
, т. Е. Не установлены ни флаги переноса, ни флаги переполнения. В приведенном примере кода main () происходит переполнение, и оба флага установлены в 1. Эта проверка не подразумевает каких-либо дальнейших вычислений, поэтому она должна быть достаточно быстрой.источник
Если у вас есть тип данных, который больше, чем тот, который вы хотите протестировать (скажем, вы делаете 32-битное добавление и у вас 64-битный тип), то это обнаружит, если произошло переполнение. Мой пример для 8-битного добавления. Но это может быть увеличено.
Он основан на концепциях, описанных на этой странице: http://www.cs.umd.edu/class/spring2003/cmsc311/Notes/Comb/overflow.html.
Для 32-битного примера
0xFF
становится0xFFFFFFFF
и0x80
становится0x80000000
и, наконец,uint16_t
становитсяuint64_t
.ПРИМЕЧАНИЕ : это ловит целочисленные переполнения сложения / вычитания, и я понял, что ваш вопрос связан с умножением. В этом случае, разделение, вероятно, лучший подход. Это - обычно способ, которым
calloc
реализации гарантируют, что параметры не переполняются, поскольку они умножены, чтобы получить окончательный размер.источник
Самый простой способ - преобразовать ваши
unsigned long
s вunsigned long long
s, выполнить умножение и сравнить результат с 0x100000000LL.Вы, вероятно, обнаружите, что это более эффективно, чем деление, как вы делали в своем примере.
О, и это будет работать как на C, так и на C ++ (как вы пометили вопрос обоими).
Просто взглянул на руководство по glibc . Есть упоминание о целочисленном переполнении trap (
FPE_INTOVF_TRAP
) как частиSIGFPE
. Это было бы идеально, если не считать неприятных моментов в руководстве:Немного стыдно на самом деле.
источник
ULONG_MAX
который легче набирать и более портативный, чем жесткое кодирование0x100000000
.long
иlong long
имеют одинаковый размер (например , на многих 64-битных компиляторов).Вот действительно быстрый способ обнаружения переполнения хотя бы для дополнений, которые могут дать преимущество для умножения, деления и мощности.
Идея состоит в том, что именно потому, что процессор просто позволит обернуть значение до нуля, а C / C ++ должен быть абстрагирован от любого конкретного процессора, вы можете:
Это гарантирует, что если один операнд равен нулю, а другой - нет, переполнение не будет обнаружено ложно и будет значительно быстрее, чем множество операций NOT / XOR / AND / test, как предлагалось ранее.
Как уже указывалось, этот подход, хотя и лучше, чем другие более сложные способы, все же является оптимизируемым. Ниже приводится ревизия исходного кода, содержащего оптимизацию:
Более эффективный и дешевый способ обнаружения переполнения умножения:
Это приводит либо к переполнению UINT32_MAX, либо к результату умножения. Это строго неопределенное поведение, чтобы позволить умножению продолжаться для целых чисел со знаком в этом случае.
источник
x+y>=256
иvalue=x+y-256
. Посколькуy<256
всегда верно, (y-256) отрицательно и поэтомуvalue < x
всегда верно. Доказательство для случая без переполнения очень похоже.uint32_t x[N], y[N], z[N], carry=0; for (int i = 0; i < N; i++) { z[i] = x[i] + y[i] + carry; carry = z[i] < (x[i] | y[i]); }
Если вы неor
укажете значения, вы не сможете различить один операнд и бит переноса, равный нулю, и один операнд, являющийся битом переноса, и равным0xffffffff
единице.Вы не можете получить доступ к флагу переполнения из C / C ++.
Некоторые компиляторы позволяют вставлять в код инструкции ловушки. На GCC вариант есть
-ftrapv
.Единственная переносимая и независимая от компилятора вещь, которую вы можете сделать, - это самостоятельно проверить наличие переполнений. Так же, как вы сделали в своем примере.
Тем не менее,
-ftrapv
кажется, ничего не делает на x86, используя последний GCC. Я предполагаю, что это пережиток старой версии или специфический для какой-то другой архитектуры. Я ожидал, что компилятор будет вставлять код операции INTO после каждого добавления. К сожалению, это не делает этого.источник
Для целых чисел без знака просто проверьте, что результат меньше, чем один из аргументов:
Для целых чисел со знаком вы можете проверить знаки аргументов и результата.
Целые числа разных знаков не могут переполняться, а целые числа одного знака переполняются, только если результат имеет другой знак:
источник
char result = (char)127 + (char)3;
будет -126; меньше, чем оба операнда.Мне нужно было ответить на этот же вопрос для чисел с плавающей запятой, где битовое маскирование и сдвиг не выглядит многообещающим. Подход, на котором я остановился, работает для чисел со знаком и без знака, целых чисел и чисел с плавающей запятой. Он работает, даже если нет более крупного типа данных, который можно использовать для промежуточных вычислений. Он не самый эффективный для всех этих типов, но поскольку он работает для всех из них, его стоит использовать.
Проверка переполнения со знаком, сложение и вычитание:
Получите константы, которые представляют наибольшее и наименьшее возможные значения для типа, MAXVALUE и MINVALUE.
Вычислить и сравнить знаки операндов.
а. Если любое значение равно нулю, то ни сложение, ни вычитание не могут переполниться. Пропустить оставшиеся тесты.
б. Если знаки противоположны, то сложение не может переполниться. Пропустить оставшиеся тесты.
с. Если знаки совпадают, вычитание не может быть переполнено. Пропустить оставшиеся тесты.
Тест на положительное переполнение MAXVALUE.
а. Если оба знака положительные и MAXVALUE - A <B, то сложение будет переполнено.
б. Если знак B отрицательный и MAXVALUE - A <-B, вычитание будет переполнено.
Тест на отрицательное переполнение MINVALUE.
а. Если оба знака отрицательны и MINVALUE - A> B, сложение будет переполнено.
б. Если знак A отрицательный и MINVALUE - A> B, вычитание будет переполнено.
В противном случае переполнения нет.
Проверка переполнения со знаком, умножение и деление:
Получите константы, которые представляют наибольшее и наименьшее возможные значения для типа, MAXVALUE и MINVALUE.
Вычислить и сравнить величины (абсолютные значения) операндов с одним. (Ниже предположим, что А и В - это величины, а не подписанные оригиналы.)
а. Если любое из значений равно нулю, умножение не может переполниться, и деление даст ноль или бесконечность.
б. Если любое из значений равно единице, умножение и деление не могут переполниться.
с. Если величина одного операнда меньше одного, а другого больше единицы, умножение не может переполниться.
д. Если обе величины меньше единицы, деление не может переполниться.
Тест на положительное переполнение MAXVALUE.
а. Если оба операнда больше одного и MAXVALUE / A <B, умножение будет переполнено.
б. Если B меньше единицы и MAXVALUE * B <A, деление будет переполнено.
В противном случае переполнения нет.
Примечание: минимальное переполнение MINVALUE обрабатывается 3, потому что мы взяли абсолютные значения. Однако если ABS (MINVALUE)> MAXVALUE, то у нас будут редкие ложные срабатывания.
Тесты на недостаточный уровень схожи, но включают EPSILON (наименьшее положительное число больше нуля).
источник
1.0e-200 / 1.0e200
будет примером фактического недостаточного значения, если предположить, что IEEE удваивается. Вместо этого правильный термин - отрицательное переполнение. </ Pedantic>1/INT_MAX
вполне может считаться недостаточным, но язык просто предписывает усечение до нуля.CERT разработал новый подход к обнаружению и составлению отчетов о переполнении целых чисел со знаком, обертке целых чисел без знака и усечении целых чисел с использованием целочисленной модели (AIR) «как будто». CERT опубликовал технический отчет с описанием модели и создал рабочий прототип на основе GCC 4.4.0 и GCC 4.5.0.
Целочисленная модель AIR либо создает значение, эквивалентное тому, которое было бы получено с использованием целых чисел с бесконечным ранжированием, либо приводит к нарушению ограничения времени выполнения. В отличие от предыдущих целочисленных моделей, целые числа AIR не требуют точных ловушек и, следовательно, не нарушают и не препятствуют большинству существующих оптимизаций.
источник
Еще одним интересным инструментом является IOC: средство проверки переполнения целых чисел для C / C ++ .
Это исправленный компилятор Clang , который добавляет проверки в код во время компиляции.
Вы получите вывод, похожий на этот:
источник
Другой вариант решения, использующий язык ассемблера, - это внешняя процедура. Этот пример для умножения целых чисел без знака с использованием g ++ и fasm под Linux x64.
Эта процедура умножает два целочисленных аргумента без знака (32 бита) (в соответствии со спецификацией для amd64 (раздел 3.2.3 Передача параметров ).
(edi и esi регистрируются в моем коде)) и возвращает результат или 0, если произошло переполнение.
Тестовое задание:
Свяжите программу с объектным файлом asm. В моем случае в Qt Creator добавьте его
LIBS
в файл .pro.источник
Рассчитать результаты с двойниками. У них есть 15 значащих цифр. Ваше требование имеет жесткую верхнюю границу для c 10 8 - оно может содержать не более 8 цифр. Следовательно, результат будет точным, если он находится в диапазоне, и в противном случае он не будет переполнен.
источник
Попробуйте этот макрос, чтобы проверить бит переполнения 32-битных машин (адаптировал решение Angel Sinigersky)
Я определил это как макрос, потому что иначе бит переполнения был бы перезаписан.
Далее следует небольшое приложение с фрагментом кода выше:
источник
_MSC_VER
хотя компиляции MS будут отклонять код).Перехват целочисленных переполнений в C указывает на более общее решение, чем обсуждаемое CERT (оно более общее с точки зрения обрабатываемых типов), даже если для этого требуются некоторые расширения GCC (я не знаю, насколько широко они поддерживаются).
источник
Я не согласен с этим. Вы могли бы написать некоторый встроенный язык ассемблера и использовать
jo
инструкцию (переход переполнения), предполагая, что вы находитесь на x86, чтобы перехватить переполнение. Конечно, ваш код больше не будет переносимым на другие архитектуры.Посмотрите
info as
иinfo gcc
.источник
Чтобы расширить ответ Head Geek, есть более быстрый способ сделать
addition_is_safe
;При этом используется безопасная машинная архитектура, в которой 64-разрядные и 32-разрядные целые числа без знака будут работать нормально. По сути, я создаю маску, которая будет маскировать все, кроме самого значимого бита. Затем я маскирую оба целых числа, и если ни у одного из них не установлен этот бит, то сложение безопасно.
Это будет еще быстрее, если вы предварительно инициализируете маску в каком-либо конструкторе, поскольку она никогда не меняется.
источник
UINT_MAX + 1
. После маскированияa
будет установлен старший бит, но1
он станет равным нулю, и, следовательно, функция вернетсяtrue
, сложение будет безопасным - и все же вы прямо на пути к переполнению.mozilla::CheckedInt<T>
предоставляет проверенную переполнением целочисленную математику для целочисленного типаT
(с использованием встроенных функций компилятора в clang и gcc, как доступно). Код находится под MPL 2.0 и зависит от трех (IntegerTypeTraits.h
,Attributes.h
иCompiler.h
) других заголовков только нестандартным библиотека заголовки плюс Mozilla конкретного утверждения машины . Возможно, вы захотите заменить механизм подтверждения, если импортируете код.источник
Ответ MSalter - хорошая идея.
Если требуется целочисленное вычисление (для точности), но имеется плавающая точка, вы можете сделать что-то вроде:
источник
(c * log(a) < max_log)
, гдеconst double max_log = log(UINT_MAX)
Набор команд x86 включает в себя инструкцию умножения без знака, которая сохраняет результат в двух регистрах. Чтобы использовать эту инструкцию из C, можно написать следующий код в 64-битной программе (GCC):
Для 32-битной программы результат должен быть 64-битным, а параметры - 32-битными.
Альтернативой является использование зависимой от компилятора встроенной функции для проверки регистра флага. Документацию GCC для встроенного переполнения можно найти в 6.56 «Встроенные функции для выполнения арифметики с проверкой переполнения» .
источник
__uint128
чтобы избежать переполнения со знаком и смещения вправо отрицательного значения.источник
Чистым способом сделать это было бы переопределение всех операторов (в частности, + и *) и проверка на переполнение перед выполнением операций.
источник
Это зависит от того, для чего вы его используете. При выполнении сложения или умножения длинных без знака (DWORD) лучшим решением будет использование ULARGE_INTEGER.
ULARGE_INTEGER - это структура двух DWORD. Полное значение может быть доступно как «QuadPart», в то время как высокий DWORD - как «HighPart», а низкий DWORD - как «LowPart».
Например:
источник
ULARGE_INTEGER
.Для выполнения беззнакового умножения без переполнения переносимым способом можно использовать следующее:
источник
Простой способ проверить наличие переполнения - выполнить проверку, проверив, меньше ли текущее значение по сравнению с предыдущим значением. Например, предположим, что у вас есть цикл для вывода степеней 2:
Добавление переполнения, проверяющего способ, который я описал, приводит к следующему:
Он работает для значений без знака, а также для положительных и отрицательных значений со знаком.
Конечно, если вы хотите сделать что-то похожее для уменьшения значений вместо увеличения значений, вы бы изменили
<=
знак, чтобы сделать это>=
, предполагая, что поведение недостаточного значения совпадает с поведением переполнения. Честно говоря, он настолько переносим, насколько вы получите без доступа к флагу переполнения ЦП (и для этого потребуется встроенный код сборки, что делает ваш код непереносимым между реализациями в любом случае).источник