Как напечатать действительно большие числа в C ++ - PullRequest
5 голосов
/ 27 октября 2008

У меня есть этот код

#include <iostream>

using namespace std;

int main(int argc,char **argv) {

    unsigned long long num1 = 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999995LL;
    unsigned long long num2 = 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999996LL;
    unsigned long long num3 = 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999997LL;
    unsigned long long num4 = 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999998LL;
    unsigned long long num5 = 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999LL;

    cout << (unsigned long long)(num1 * num2 * num3 * num4 * num5) << endl;
    return 0;
}

Как видите, числа огромны, но когда я делаю математику, я получаю следующее: 18446744073709551496

Во время компиляции я получаю следующие предупреждения:

warning: integer constant is too large for its type|
In function `int main(int, char**)':|
warning: this decimal constant is unsigned only in ISO C90|
...

Ответы [ 7 ]

21 голосов
/ 27 октября 2008

Ваш результат больше, чем тип long long - вам нужно посмотреть на BigInteger или библиотеку произвольной точности, что-то вроде gmp

7 голосов
/ 27 октября 2008

Эти числа не будут вписываться ни в какие типы данных C ++. Если вы просто хотите распечатать их, сохраните числа в строку. Если вы хотите заняться математикой, найдите математическую библиотеку произвольной точности и используйте ее.

3 голосов
/ 28 октября 2008

Что вы пытаетесь сделать? Вы понимаете основы двоичных и десятичных чисел? Почему 8 битов содержат только значения от 0 до 255, 12 битов от 0 до 4095 и т. Д.? Сколько бит нужно, чтобы удержать интересующее вас число? Или лучше, сколько из числа вы заинтересованы в создании? И вы используете 9, чтобы увеличить число? А как насчет hex 0xF ... вместо этого? Если вам нужно наибольшее число без знака (в пределах одного из стандартных целочисленных типов), почему бы и нет:

без знака long long a, b;

а = -1; // что выглядит неправильно при смешивании со знаком и без знака, но это действительно, число преобразуется в без знака перед сохранением

b = 0; В-; // делает то же самое, что и выше

Тебе действительно нужна точность на этом уровне? Вы понимаете, что умножение может потребовать результата, в два раза превышающего размер каждого операнда? 0xFF * 0xFF = 0xFE01, если в этом случае вы использовали 8-битные целые числа, вы не могли бы выполнить математику. Это только ухудшается, когда вы продолжаете умножать 0xFF * 0xFF * 0xFF = 0xFD02FF.

Что пытаетесь сделать?


Видя ваш ответ:

Я не видел Эйлера № 8 раньше. Звучит как хороший вопрос для интервью, поскольку для его решения требуется всего несколько строк кода.


Ваш другой ответ:

Цифры ...

Вероятно, из-за того, что у нас есть 10 пальцев (и, возможно, 10 пальцев на ногах), мы растем с "основанием 10". Наши часы по большей части основаны на 60, но они были смешаны с основанием 10, чтобы сделать его более запутанным. В любом случае, основание 10 означает, что для каждого заполнителя номера у вас есть одна из 10 уникальных цифр, и когда вы достигнете максимума в этом месте, вы перейдете на следующее место. Это все начальная школа.

000
001
002
003
...
008
009
010
011
012
...

Посмотрите, как самая правая цифра имеет 10 символов (0,1,2,3,4,5,6,7,8,9), и когда она достигает последнего символа, она начинается снова и слева от увеличивается на единицу. Это правило справедливо для всех базовых систем нумерации.

Это верно для базы 2, за исключением того, что есть только два символа, 0 и 1

000
001
010
011
100
101
...

То же самое верно для восьмеричного, но 8 символов (0,1,2,3,4,5,6,7)

000
001
002
003
004
005
006
007
010
011
012
013
...

То же самое относится и к шестнадцатеричным 16 символам (0,1,2,3,4,5,6,7,8,9, a, b, c, d, e, f)

000
001
002
003
004
005
006
007
008
009
00а
00b
00c
00D
00E
00f
010
011
012
013
...

Я собирался вникнуть в суть использования двоичного кода на других базах (например, 10) в компьютерах. Суть в том, что можно легко включить или выключить два состояния, либо высокое и низкое. Два состояния подобны двум символам 1 и 0 в базе 2. Попытка держать электронику настроенной более чем на два состояния в пределах доступного напряжения является жесткой, по крайней мере, так было раньше, поддерживая ее около нуля или выше некоторого небольшого числа вольт, Относительно просто, поэтому цифровая электроника использует два состояния, двоичное.

Даже простое задание для человека в двоичном коде является многословным, простая математика второго класса - все еще множество единиц и нулей. Итак, восьмеричное стало популярным, потому что оно позволяло вам мыслить группами по три бита, и вы могли использовать знакомые нам символы в виде чисел 0,1,2,3,4,5,6,7. Но группы из четырех, что является еще одной степенью 2, дает людям гораздо больше умственных вычислительных возможностей, чем восьмеричные, ведь гекс основан на 4 битах, что также является степенью 2. Нам пришлось добавить больше символов к 10, которые мы позаимствовали у Традиционная арабская база 10, поэтому были использованы первые 6 алфавита. Восьмеричное редко, если вообще используется, вы можете сказать кому-то возраст, если они думают, что восьмеричное, а не шестнадцатеричное. (Я из поколения гекс, но работал с теми из восьмеричного поколения, которые борются с гексом, потому что они не могут перейти от восьмеричного к двоичному в гекс в своем уме).

База 10 в компьютере похожа на обычное человеческое мышление в гексе. компьютеры не делают основание 10 (хорошо для ленивых людей, которых они использовали для bcd), они делают основание 2. Десятичное число 1234 в компьютере действительно 0x4D2 или 0b010011010010. Это как значение, скажем, вы хотите добавить 1234 плюс другое число, которое вам нужно, это значение, которое не имеет ничего общего с символами 1, 2, 3 и 4. Но чтобы опубликовать этот ответ в stackoverflow, мы не используем число, которое мы используйте ASCII, поэтому 1234 в ascii - это 0x31, 0x32, 0x33, 0x34, что важно знать для вашего решения euler, предполагая, что 1000-значное число было предоставлено в виде строки ascii, какой она должна быть, или вам придется преобразовать ее от бинарного к ascii, поскольку проблема - это проблема базы 10, а не базы 2 по определению.

Итак, вернемся к тому, что я спросил. Скажем, у вас было 4 бита памяти для хранения числа, насколько большое число вы могли бы хранить? Если вы думаете, что только основание 10, вы можете подумать, что число - это 9, потому что вы научились использовать самый большой символ в каждом месте хранения, 99999 - это самое большое число, если у вас есть 5 мест хранения в базе 10. Назад к четырем битам хотя самый большой символ для одного бита равен 1, поместите это число в каждую ячейку памяти, которую вы получите, 1111 (четыре). Просто взглянув на эти четыре, вы в уме сможете легко увидеть восьмеричную и восьмеричную версию того же восьмеричного числа или шестнадцатеричного шестнадцатеричного числа. Чтобы увидеть десятичное число требует математики, или в этом случае запоминания, это число 15 десятичных. Итак, самое большое четырехбитное число, которое вы можете иметь, это 0xF или 15, а не 9. Как насчет 8-битного числа? 0xFF или 255 (от 2 до 8 степени минус один). Самый большой 16-битный номер? 65535 и т. Д.

Поэтому, когда я спрашиваю, сколько бит вы пытаетесь использовать, я это имею в виду. Посмотрите на это число 99999. Опять 10, вы думаете, что это самое большое число, но для компьютера это только часть пути, десятичное число 99999 равно 0x1869F, для хранения которого требуется 17 бит памяти, самое большое 17-битное число, которое вы можете store это 0x1FFFF, то есть 131071, что немного больше, чем 99999. Поэтому, когда вы хотите думать о больших цифрах и математике на компьютере, вы должны думать о двоичном (или шестнадцатеричном).

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

Возьмем наибольшее 4-битное число 1111 (двоичное), которое является 15 десятичным. Добавьте это с наибольшим четырехбитным числом, и вы получите 15 + 15 = 30 = 0x1E или 11110 двоичного числа. Таким образом, чтобы сложить два четырехбитных числа, вам нужно пять битов, чтобы сохранить ваш ответ. Компьютеры держат бит «неси» для этого дополнительного бита. По существу, математические функции сложения / вычитания в компьютере позволяют вам иметь N + 1 бит. Так что, если это 32-битный компьютер, у вас есть 33 бита для добавления / подсчета.

Проблема заключается в умножении и делении, которое даже сегодня многие процессоры не поддерживают (да, у многих нет fpu и они только складывают и вычитают, иногда умножают, но делят редко. Умножение и деление отнимают много электроники и компромисс это вы можете делать их с добавлением и вычитанием в программном обеспечении). Возьмите умножение наихудшего случая для четырехбитной системы 1111 * 1111 = 11100001, поэтому для сохранения результата 4-битного умножения требуется 8 битов, вы быстро обнаружите, что если у вас есть 4-битная система MOST умножений, которые вы хотите сделать, то получится число, которое не может быть сохранено в 4 биты. Поэтому, когда я увидел, что вы взяли 64-битные целые числа (длинный без знака длинный обычно 64-битный) и умножили четыре раза, это означает, что вам нужно 64 * 5 или 320-битное целое число для хранения вашего ответа, вы пытались поместить этот ответ в 64 большой результат, который довольно часто, в зависимости от компилятора и компьютера, будет радостно выполнять и обрезать верхние биты, оставляя вас с младшими 64 битами результата, который может легко выглядеть меньше, чем любой из ваших операндов, как я и думал Вы могли бы сделать вначале.

Плавающая точка не намного больше, чем научная запись, но в двоичном формате, если вы хотите умножить числа 1234 и 5678, используя научную запись, вы бы взяли 1,234 * 10 ^ 3, умноженное на 5,687 * 10 ^ 3, и получили бы 7,007 * 10 ^ 6. , Вы сохраняете свою точность и способны представлять более широкий диапазон чисел. Я не буду вдаваться в то, как это работает в двоичном формате. Но это не работает для вашего первоначального вопроса.

Ах, последнее, что нужно прояснить, что я делал в своем вопросе / ответе. Отрицательные целые числа в двоичном коде. Из-за отношений между сложением и вычитанием и базовыми системами вы можете поиграть в некоторые трюки. Скажем, я хотел вычесть 1 из числа 7 (десятичного) с помощью двоичного файла. Ну, нет такой вещи, как схема вычитания, вы вместо этого добавляете отрицательное число, поэтому вместо 7 - 1 это действительно 7 + (-1), это имеет значение:

0111 + ???? = 0110

Какое число вы можете добавить к 7, чтобы получить 6 ... в двоичном виде?

0111 + 1111 = 0110

Отрицательные числа в двоичном коде называются «дополнением до двух», короче говоря, ответ «инвертировать и добавить 1». Как вы представляете минус 1 в двоичном? возьмите плюс один 0001, затем инвертируйте его, то есть сделайте единичные нули и нули (также известные как дополнения) 1110, затем добавьте один 1111. Минус один - это специальное число в компьютерах (хорошо везде), независимо от того, сколько битов у вас есть представлен как все. Поэтому, когда вы видите, кто-то делает это:

unsigned char a;

а = -1;

Компилятор сначала смотрит на это -1 и думает ... 11111 (двоичный), затем он смотрит на знак равенства, а другая сторона, о, вы хотите, чтобы все были единицами, видит, что у вас есть целое число со знаком и без знака, но преобразование состоит в том, чтобы просто переместить биты так, как вы сказали выше, что вы хотите a = 0xFF; (предполагается, что 8-битный символ без знака).

Некоторые компиляторы могут жаловаться на то, что вы пытаетесь сохранить отрицательное число в неподписанном числе. Другие компиляторы будут смотреть на это -1 и видеть его как 32-битную или в наши дни, может быть, 64-битную целочисленную константу со знаком, а затем, когда он оценивает равно как 8-битный без знака, вы получите предупреждение о том, что вы не можете сохранить -1 в подписанной или беззнаковый символ без опечатки. Но если вы сделаете это:

а = 0; а -;

Это понравится всем компиляторам. и не будет жаловаться, он просто записывает вычислительные циклы во время выполнения, а не во время компиляции.

Теперь где-то мой друг рассказал мне о книге, которая последовательно выполняет двоичную математику. Например, чтобы отменить число, обычно вы делаете трюк с инвертированием и одним трюком, но с карандашом и бумагой некоторые могут сказать вам другой трюк. Начиная справа, скопируйте нули до и включая первую 1, затем инвертируйте после этого, так что минус 2

0010
1110

Начиная справа, скопируйте 0, затем первый, затем инвертируйте оставшиеся биты, когда идете налево.

минус 6

0110
1010

минус 4

0100
1100

Предположительно, есть хитрости для сложения и вычитания (ну, это легко), но также умножение и деление. Если вы делаете их последовательно, то вы можете делать бесконечно длинную математику в двоичном с тем же alu. Если бы вы знали, как это сделать, вы могли бы реализовать это в программном обеспечении, и ваш оригинальный вопрос умножения больших констант (с предположением сохранения всей точности) тривиален на любом компьютере.

3 голосов
/ 27 октября 2008

Если вы хотите, чтобы литералы были такими большими в вашем коде, вы должны будете ввести их как строковые литералы и загрузить их в некоторый класс BigInt. Сейчас нет способа выразить такие большие целочисленные литералы в исходном коде (хотя, надеюсь, C ++ 0x устранит этот недостаток).

Если вы используете библиотеку BigInteger , взгляните на функцию stringToBigUnsigned в BigIntegerUtils.hh для построения большого целого числа из строки.

#include "BigUnsigned.hh"
#include "BigIntegerUtils.hh"     

 BigUnsigned  num1 = stringToBigUnsigned (
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
    "99999999999999999999999999999999999995"
    );
1 голос
/ 27 октября 2008

Ответ, который вы получили, 18446744073709551496, связан с тем, что ваши 999 ... 9 были усечены при назначении длинного лонг, плюс переполнение нескольких операций. Это детерминированный, но фактически просто случайный набор битов.

0 голосов
/ 17 сентября 2012

Числа не могут вписаться в диапазон unsigned long long, поэтому либо вы можете использовать библиотеку GMP, либо использовать строку для представления больших чисел, как я сделал для вычисления факториала числа, например 50:

http://codepad.org/bkWNV0JC

#include <cmath>
#include <iostream>
using namespace std;
int main()
{
  unsigned int nd, nz;   
  unsigned char *ca;   
  unsigned int j, n=50, q, temp;
  int i;
  double p;
    p = 0.0;
    for(j = 2; j <= n; j++)
    {
      p += log10((double)j);  
    }
    nd = (int)p + 1;

    ca = new unsigned char[nd+1];
    if (!ca)
    {
      cout << "Could not allocate memory!!!";
      exit(0);
    }
    for (i = 1; (unsigned)i < nd; i++)
    {
      ca[i] = 0;
    }
    ca[0] = 1;

    p = 0.0;
    for (j = 2; j <= n; j++)
    {
      p += log10((double)j);   
      nz = (int)p + 1;        
      q = 0;                  
      for (i = 0;(unsigned) i <= nz; i++)
      {
        temp = (ca[i] * j) + q;
        q = (temp / 10);
        ca[i] = (char)(temp % 10);
      }
    }

    cout << "\nThe Factorial of " << n << " is: ";
    for( i = nd - 1; i >= 0; i--)
    {
      cout << (int)ca[i];
    }
  //  delete []ca;    
  return 0;
}
0 голосов
/ 27 октября 2008

unsigned int представляет системное слово. Сегодня это слово будет максимум до 2 ^ 32 -1 или 2 ^ 64 - 1, в зависимости от того, 32-битная или 64-битная система. Ты бьешь по кепке.

Вы должны написать класс bignum или использовать его вне сети.

Почему вы все равно решаете эту проблему?

...