Добавление больших чисел в C - PullRequest
2 голосов
/ 28 марта 2011

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

unsigned long total = 0;
total = 1124073472 + 2835349503;
total += 2533359615;
printf("Total: %u\n", total);

Сумма, напечатанная выше, неверна.Первый результат добавления хорош, но третье добавление сбрасывает общее количество.Я думаю, что это из-за переполнения.Мой вопрос, что является возможным решением?Есть ли решение без использования сторонних библиотек?

Примечание: я пробовал разные типы данных для общего количества.Некоторые из них: DWORD64, INT64, LONG64 и т. Д.

Заранее спасибо.

Ответы [ 9 ]

3 голосов
/ 28 марта 2011

Математическая сумма этих трех чисел больше, чем 2 32 , и вы используете unsigned long, который 32-разрядный в Windows (даже если вы компилируете свою программу с 64-битными указателями -- это преднамеренное нарушение C89 со стороны Microsoft), поэтому стандарты C и C ++ указывают, что он оборачивается.Полученное число: 1124073472 + 2835349503 + 2533359615 - 2 32 .

Если вы используете 64-битный тип, вам нужно изменить спецификатор формата printf, чтобы он соответствовал.Этот код даст математически ожидаемый ответ в системе, совместимой с C99:

#include <stdio.h>

int main(void)
{
    unsigned long long total = 1124073472 + 2835349503;
    total += 2533359615;
    printf("Total: %llu\n", total);
    return 0;
}

Однако ни одна версия MSVC не совместима с C99.Я не знаю подходящего объявления типа или спецификатора формата printf для MSVC.

Дополнения: 1) Добавление не определено, чтобы обернуться при переполнении для подписанных типов;это неопределенное поведение.Это чаще всего кусает людей, которые используют индексы со знаком цикла, когда ограничение цикла зависит от данных.2) Если бы вы занимались математикой с числами, которые потенциально могли бы быть больше, чем 2 64 , даже long long не спасло бы вас.C и (насколько я знаю) C ++ не имеют настоящих типов "bignum";вам понадобится сторонняя библиотека.

2 голосов
/ 28 марта 2011

Вы правы, это из-за переполнения.Использование 64-битного типа исправит это - вам нужно будет изменить строку формата printf() на 64-битный тип printa.Если сделать total unsigned long long, то вы получите 64-битный тип, но самый безопасный способ - использовать один из typedef из stdint.h.Вот пример программы - я также использовал inttypes.h, чтобы получить PRIu64.

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

int main(int argc, char **argv)
{
  uint64_t total = 0;
  total = 1124073472 + 2835349503;
  total += 2533359615;
  printf("Total: %"PRIu64"\n", total);

  return 0;
}
1 голос
/ 11 декабря 2011

Ну, почти (!) Все правы.Я думаю, что вы можете посмотреть на две страницы.Это действительно даст вам лучшее представление о том, как вы можете обрабатывать действительно БОЛЬШИЕ числа:

0 голосов
/ 29 марта 2011

Ваш код содержит ошибки. Вы должны заменить %u на %lu в вашем выражении printf, чтобы соответствовать типу total. Этого достаточно, если вы работаете в системе с 32-битными значениями и длиной 64 бита. Но если вы используете какую-то другую систему, вам, возможно, придется использовать unsigned long long, как отмечено в некоторых других ответах.

0 голосов
/ 28 марта 2011

Код имеет две проблемы:

  1. Беззнаковый long имеет только 32 бита в 32-битных системах. Вам нужно использовать long long (и включить стандарт C99 в настройках компилятора) или INT64 или что-то в этом роде.

  2. % u в строке формата printf предназначен для чисел шириной 32 (или менее). Вместо этого вам нужно использовать% llu.

0 голосов
/ 28 марта 2011

Следующий код должен работать под Visual C ++:

unsigned __int64 total = 0;
total = 1124073472LL + 2835349503LL;
total += 2533359615LL;
printf("Total: %I64u\n", total);
0 голосов
/ 28 марта 2011

Это действительно переполнение. Возможно, вы захотите создать свой собственный класс «BigNumber» для выполнения арифметических операций с произвольно большими числами. Тем не менее, я бы рекомендовал использовать библиотеку, если это возможно. Написание своего собственного класса "BigNumber" может быть немного тяжелой работой. Ознакомьтесь с теми, которые размещены здесь - Класс для вычисления произвольно больших чисел?

Если вы их не используете, вы можете по крайней мере использовать их в качестве руководства.

0 голосов
/ 28 марта 2011

Вы пробовали "unsigned long long int"? Это, безусловно, будет 64 бита, или используйте uint64_t, если у вас есть stdint.h. Конечно, вы можете столкнуться с новыми проблемами переполнения (это действительно так), когда вы выбираете еще большие числа, а затем пришло время использовать библиотеку bignum, такую ​​как gmp. И сделайте проверку переполнения перед добавлением, если вы придерживаетесь 64-битных чисел. И используйте «% lld» для печати десятичного результата в этом случае.

0 голосов
/ 28 марта 2011

То, что вы видите, действительно из-за переполнения, но в типе печати, как уже упоминали другие, поскольку это число вписывается в long.

Вы сможете выполнять операции, которые соответствуют типу данных long. Если вы хотите пойти больше, чем вам, вам понадобится какая-то функция «Big Integer», и если вы не хотите использовать стороннюю библиотеку, вы можете реализовать свою собственную, однако вы бы заново изобрели колесо и вам лучше с использованием проверенной библиотеки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...