[Решено] Проблема с unsigned int

Модератор: Модераторы разделов

Аватара пользователя
Janik
Сообщения: 870
Статус: Оператор вычислительных машин
ОС: Debian

[Решено] Проблема с unsigned int

Сообщение Janik »

Написал следущий код (вычисляет факториал любого натурального числа). Всё хорошо, но пока аргумент не больше 33. При n>=34 показывает ноль. Что я делаю не так?

Код: Выделить всё

#include <stdio.h>

void main (int argc, char **argv)
{
    unsigned int n, i = 1, result = 1;

    if (argc < 2)
        return;

    n = atoi (argv[1]);

    while (1)
    {
        printf ("%d\n", i);
        result *= i++;
        if (i > n)
            break;
    }

    printf ("%u! = %u\n", n, result);
}
Кто ищет, тот всегда найдет!
Опыт - это когда все получается с первого раза.
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: [Решено] Проблема с unsigned int

Сообщение drBatty »

очевидно 34! не влезает в unsigned
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
FatZer
Сообщения: 33
ОС: Gentoo

Re: [Решено] Проблема с unsigned int

Сообщение FatZer »

хочу вас расстроить: правильный результат у вас только до 13-ти.
т.к. разрядной сетки int'а просто не хватает для хранения больших чисел.
попробуйте печатать результаты по ходу вычислений.
Спасибо сказали:
Аватара пользователя
Janik
Сообщения: 870
Статус: Оператор вычислительных машин
ОС: Debian

Re: [Решено] Проблема с unsigned int

Сообщение Janik »

Блин, теперь понял: 34! = 2^32.
Кстати, а как выводить тип long long? А то у меня показывает ноль при выводе типом unsigned long long
Кто ищет, тот всегда найдет!
Опыт - это когда все получается с первого раза.
Спасибо сказали:
FatZer
Сообщения: 33
ОС: Gentoo

Re: [Решено] Проблема с unsigned int

Сообщение FatZer »

Janik писал(а):
23.04.2012 17:47
Кстати, а как выводить тип long long? А то у меня показывает ноль при выводе типом unsigned long long

вообще говоря, зависит от компилятора...
для gcc должен быть 64-х битным на пост-интеловских архитектурах...
см. sizeof(long long)
Спасибо сказали:
Аватара пользователя
Janik
Сообщения: 870
Статус: Оператор вычислительных машин
ОС: Debian

Re: [Решено] Проблема с unsigned int

Сообщение Janik »

У меня только 32-х битный
Кто ищет, тот всегда найдет!
Опыт - это когда все получается с первого раза.
Спасибо сказали:
FatZer
Сообщения: 33
ОС: Gentoo

Re: [Решено] Проблема с unsigned int

Сообщение FatZer »

Janik писал(а):
23.04.2012 18:22
У меня только 32-х битный

Код: Выделить всё

alexander@goblin /tmp $ cat 1.c
#include <stdio.h>
int main (int argc, char **argv)
{
        printf ("%u %u\n", sizeof(long long), sizeof(long));
}
alexander@goblin /tmp $ gcc -m32 1.c
alexander@goblin /tmp $ ./a.out
8 4
alexander@goblin /tmp $ gcc -m64 1.c
alexander@goblin /tmp $ ./a.out
8 8
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: [Решено] Проблема с unsigned int

Сообщение drBatty »

Janik
хватит заниматься ерундой.
вот смотрите:

Shell

$ bc bc 1.06.95 Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc. This is free software with ABSOLUTELY NO WARRANTY. For details type `warranty'. define f (x) { if (x <= 1) return (1); return (f(x-1) * x); } f(1) 1 f(10) 3628800 f(13) 6227020800 f(34) 295232799039604140847618609643520000000 2^32 4294967296 2^64 18446744073709551616


ещё вопросы?
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: [Решено] Проблема с unsigned int

Сообщение drBatty »

Janik писал(а):
23.04.2012 17:47
Кстати, а как выводить тип long long? А то у меня показывает ноль при выводе типом unsigned long long

эх... читать маны уже не модно... man 3 printf
The length modifier
Here, "integer conversion" stands for d, i, o, u, x, or X conversion.

hh A following integer conversion corresponds to a signed char or unsigned char argument, or a follow‐
ing n conversion corresponds to a pointer to a signed char argument.

h A following integer conversion corresponds to a short int or unsigned short int argument, or a fol‐
lowing n conversion corresponds to a pointer to a short int argument.

l (ell) A following integer conversion corresponds to a long int or unsigned long int argument, or a
following n conversion corresponds to a pointer to a long int argument, or a following c conversion
corresponds to a wint_t argument, or a following s conversion corresponds to a pointer to wchar_t
argument.

ll (ell-ell). A following integer conversion corresponds to a long long int or unsigned long long int
argument, or a following n conversion corresponds to a pointer to a long long int argument.

L A following a, A, e, E, f, F, g, or G conversion corresponds to a long double argument. (C99
allows %LF, but SUSv2 does not.)

q ("quad". 4.4BSD and Linux libc5 only. Don't use.) This is a synonym for ll.

j A following integer conversion corresponds to an intmax_t or uintmax_t argument.

z A following integer conversion corresponds to a size_t or ssize_t argument. (Linux libc5 has Z
with this meaning. Don't use it.)

t A following integer conversion corresponds to a ptrdiff_t argument.

The SUSv2 only knows about the length modifiers h (in hd, hi, ho, hx, hX, hn) and l (in ld, li, lo, lx,
lX, ln, lc, ls) and L (in Le, LE, Lf, Lg, LG).
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
Janik
Сообщения: 870
Статус: Оператор вычислительных машин
ОС: Debian

Re: [Решено] Проблема с unsigned int

Сообщение Janik »

drBatty писал(а):
23.04.2012 19:33
хватит заниматься ерундой.

Понимаю, что велосипед. Просто изучаем сейчас комбинаторику и, соответственно, факториал.

Код немного изменил. Теперь как, правильно считает?

Код: Выделить всё

#include <stdio.h>

void main (int argc, char **argv)
{
    unsigned long long int n, i = 1, result = 1;

    if (argc < 2)
        return;

    n = atoi (argv[1]);

    while (1)
    {
        result *= i++;
        if (i > n)
            break;
        printf ("%llu! = %llu\n", i, result);
    }
}


Код: Выделить всё

factorial 35
2! = 1
3! = 2
4! = 6
5! = 24
6! = 120
7! = 720
8! = 5040
9! = 40320
10! = 362880
11! = 3628800
12! = 39916800
13! = 479001600
14! = 6227020800
15! = 87178291200
16! = 1307674368000
17! = 20922789888000
18! = 355687428096000
19! = 6402373705728000
20! = 121645100408832000
21! = 2432902008176640000
22! = 14197454024290336768
23! = 17196083355034583040
24! = 8128291617894825984
25! = 10611558092380307456
26! = 7034535277573963776
27! = 16877220553537093632
28! = 12963097176472289280
29! = 12478583540742619136
30! = 11390785281054474240
31! = 9682165104862298112
32! = 4999213071378415616
33! = 12400865694432886784
34! = 3400198294675128320
35! = 4926277576697053184
Кто ищет, тот всегда найдет!
Опыт - это когда все получается с первого раза.
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: [Решено] Проблема с unsigned int

Сообщение drBatty »

drBatty писал(а):
23.04.2012 19:33
f(34)
295232799039604140847618609643520000000

Janik писал(а):
24.04.2012 13:49
34! = 3400198294675128320

разницу не видите??

всё потому-что:
drBatty писал(а):
23.04.2012 19:33
2^64
18446744073709551616

http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
sash-kan
Администратор
Сообщения: 13939
Статус: oel ngati kameie
ОС: GNU

Re: [Решено] Проблема с unsigned int

Сообщение sash-kan »

Janik писал(а):
24.04.2012 13:49
2! = 1
3! = 2
4! = 6
ну, вообще-то:
1! = 1
2! = 2
3! = 6

а на шаге
Janik писал(а):
24.04.2012 13:49
22! = 14197454024290336768
уже точно неправильный результат даже на глаз — в конце числа должны быть нули·
Писать безграмотно - значит посягать на время людей, к которым мы адресуемся, а потому совершенно недопустимо в правильно организованном обществе. © Щерба Л. В., 1957
при сбоях форума см.блог
Спасибо сказали:
Аватара пользователя
Janik
Сообщения: 870
Статус: Оператор вычислительных машин
ОС: Debian

Re: [Решено] Проблема с unsigned int

Сообщение Janik »

Короче, вывод: не изобретай велосипед, а используй bc.
Кто ищет, тот всегда найдет!
Опыт - это когда все получается с первого раза.
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: [Решено] Проблема с unsigned int

Сообщение drBatty »

Janik писал(а):
24.04.2012 19:53
Короче, вывод: не изобретай велосипед, а используй bc.

вообще-то в этом посте: [Решено] Проблема с unsigned int
я вам уже привёл рабочую программу. Она и на С работает.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: [Решено] Проблема с unsigned int

Сообщение NickLion »

Если очень хочется Си и не морочить голову с ручной реализацией длинной арифметики (хотя для данной задачи там всё просто), то можно так:

Код: Выделить всё

#include <stdio.h>
#include <gmp.h>

int main(void)
{
    unsigned int n, i;
    char buf[256];
    scanf("%u", &n);
    mpz_t fctr;
    mpz_init_set_ui(fctr, 1);
    for (i = 2; i <= n; i++) {
        mpz_mul_ui(fctr, fctr, i);
        printf("%u!: %s\n", i, mpz_get_str(buf, 10, fctr));
    }
    mpz_clear(fctr);
    return 0;
}


Компилить: gcc 1.c -o 1 -lgmp

UPD (немного код переделал)
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: [Решено] Проблема с unsigned int

Сообщение drBatty »

NickLion писал(а):
25.04.2012 09:01
(хотя для данной задачи там всё просто)

не так уж и просто - умножение получается завязанным на конкретную архитектуру CPU. Как сделать быстрое и переносимое длинное умножение, лично мне не известно. Может вы знаете?

ЗЫЖ кстати в данном конкретном случае можно и не мучится, а просто выводить НЕХ (NaN) для всех факториалов, которые не влезают в конкретный тип. А в качестве типа можно юзать просто uint64_t, который определён в C99 7.18.1.1.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: [Решено] Проблема с unsigned int

Сообщение NickLion »

drBatty писал(а):
25.04.2012 09:58
не так уж и просто - умножение получается завязанным на конкретную архитектуру CPU. Как сделать быстрое и переносимое длинное умножение, лично мне не известно. Может вы знаете?

В том и прикол, что длинное умножение тут не нужно (в плане умножение длинного длинное, только длинное на короткое).
Вот на скорую руку сваял (решение не самое оптимальное — умножаю зарезервированные нули):

Код: Выделить всё

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

#define BASE 10000
#define BASE_LOG 4

typedef struct
{
    unsigned int count;
    unsigned int* num;
} big;

void big_atleast(big* x, unsigned int sz)
{
    if (x->count < sz) {
        unsigned int nc = 1, i;
        while (nc < sz)
            nc <<= 1;
        unsigned int* nn = (unsigned int*)malloc(nc * sizeof(unsigned int));
        for (i = 0; i < x->count; i++)
            nn[i] = x->num[i];
        for (; i < nc; i++)
            nn[i] = 0;
        x->count = nc;
        free((void*)x->num);
        x->num = nn;
    }
}

void big_init(big* x, unsigned int n)
{
    unsigned int i;
    x->count = 0;
    x->num = NULL;
    big_atleast(x, 4);
    for (i = 0; n; i++, n /= BASE) {
        x->num[i] = n % BASE;
    }
}

void big_mul(big* x, unsigned int n)
{
    unsigned int c = 0, i, t;
    for (i = 0; i < x->count; i++) {
       t = x->num[i] * n + c;
       x->num[i] = t % BASE;
       c = t / BASE;
    }
    if (c) {
        big_atleast(x, x->count+1);
        x->num[i] = c;
    }
}

void big_print(big* x)
{
    int i;
    int q = 0;
    for (i = (int)x->count - 1; i >= 0 && !x->num[i]; i--) { }
    if (i < 0)
         i = 0;
    printf("%u", x->num[i--]);
    for (; i >= 0; i--) {
        printf("%0*u", BASE_LOG, x->num[i]);
    }
}

void big_free(big* x)
{
    free((void*)x->num);
}

int main(void)
{
    unsigned int n, i;
    scanf("%u", &n);
    big fctr;
    big_init(&fctr, 1);
    for (i = 2; i <= n; i++) {
        big_mul(&fctr, i);
        printf("%u!: ", i);
        big_print(&fctr);
        printf("\n");
    }
    big_free(&fctr);
}


Данное решение не завязано на конкретную архитектуру CPU. big_mul работает только для умножения на числа не более BASE. BASE выбирается таким, чтобы BASE*BASE влазило в int — только это зависимо.
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: [Решено] Проблема с unsigned int

Сообщение drBatty »

NickLion писал(а):
25.04.2012 11:35
t = x->num[i] * n + c;
x->num[i] = t % BASE;
c = t / BASE;

вот я не думаю, что такой пассаж будет работать так же быстро, как и команда imul процессора. ИМХО раз так в 10..100 медленнее. Хотя если компилятор таки распарсит, что t % BASE === t & (BASE - 1), то да, взлетит более-менее. Хотя всё равно медленно, ибо даже 32 битный i80386 куда как быстрее множил на 32х битное число. По крайней мере раз в 10 быстрее, чем ваш код.
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: [Решено] Проблема с unsigned int

Сообщение NickLion »

drBatty писал(а):
25.04.2012 11:46
NickLion писал(а):
25.04.2012 11:35
t = x->num[i] * n + c;
x->num[i] = t % BASE;
c = t / BASE;

вот я не думаю, что такой пассаж будет работать так же быстро, как и команда imul процессора. ИМХО раз так в 10..100 медленнее. Хотя если компилятор таки распарсит, что t % BASE === t & (BASE - 1), то да, взлетит более-менее. Хотя всё равно медленно, ибо даже 32 битный i80386 куда как быстрее множил на 32х битное число. По крайней мере раз в 10 быстрее, чем ваш код.

Во-первых, BASE десятичное, чтобы с переводом в десятичный вид не мучаться. Как следствие, заменить & не получится в принципе.
Во-вторых, работа с памятью может стоить 50 умножений.
В-третьих, в ассемблерном коде big_mul нет ни одного деления, сейчас лень разбираться что к чему, но суть в том, что оптимизирует компилер очень неплохо.
В-четвёртых, не будет выигрыша в 10-100 раз.
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: [Решено] Проблема с unsigned int

Сообщение drBatty »

NickLion писал(а):
25.04.2012 12:01
Во-первых, BASE десятичное, чтобы с переводом в десятичный вид не мучаться. Как следствие, заменить & не получится в принципе.

плохо... Плохо для быстродействия.
NickLion писал(а):
25.04.2012 12:01
Во-вторых, работа с памятью может стоить 50 умножений.

не в этом случае. Всё это будет лежать в регистрах, которых сейчас штук 200 ЕМНИП, или на край в первом кеше, который вроде-бы работает на частоте CPU. Т.ч. ИМХО есть за что бороться.
NickLion писал(а):
25.04.2012 12:01
В-третьих, в ассемблерном коде big_mul нет ни одного деления, сейчас лень разбираться что к чему, но суть в том, что оптимизирует компилер очень неплохо.

это старый хакерский трюк (:
Связано это с тем, что деление на константу, есть умножение на обратную константу. Если числа целые, то обратная константа <1, но её можно предварительно домножить на 2^M, а потом результат поделить на 2^M, такое умножение/деление со времён i8086 выполняется за 1 такт, а со времён iPentium ещё быстрее. Простое умножение (не на константу) тоже делается очень быстро, это уже много десятков лет совсем не сложение и сдвиг побитно.
Например, если у нас 16 бит, то деление на 3 эквивалентно умножению на 21845, при этом надо в качестве частного брать сдвинутый на 16 бит результат (что не занимает времени, т.к. результат получается в виде двух чисел). С остатком тяжелее, ибо он получается тоже делённый на 3, и его приходится опять умножать.
NickLion писал(а):
25.04.2012 12:01
В-четвёртых, не будет выигрыша в 10-100 раз.

а я проверял. на каком-то i80586, уже и не помню, на каком точно. Уверен, что на современном CPU выигрыш будет ещё больше (особенно если заюзать SSE, и множить аж по 128 бит за раз).
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: [Решено] Проблема с unsigned int

Сообщение NickLion »

С SSE верю, что можно 10 (и может более) кратного ускорения достичь, но остальное — ну, 2-3 раза, вряд ли выше. На старых процах, конечно, разница могла быть больше, там умножение по 15 тактов занимало, сейчас сомневаюсь.

Любой код вручную можно на чистом асме ускорить. Но будет ли это того стоить?

А по поводу 10-го основания — если часто нужно переводить в 10-ю систему, то такой вариант будет эффективнее.
Спасибо сказали: