Форум программистов, компьютерный форум CyberForum.ru

Вычисления с малыми числами - C++

Восстановить пароль Регистрация
 
Рейтинг: Рейтинг темы: голосов - 9, средняя оценка - 4.78
Telliax
0 / 0 / 0
Регистрация: 11.01.2010
Сообщений: 5
11.01.2010, 16:18     Вычисления с малыми числами #1
Приветсвую, комрады. Столкнулся со следующей проблемой: при сложении двух чисел порядков е-12 и е-25 упорно в качесте результата получаю большее из чисел. Я так понимаю таким образом С++ "округляет". И черт бы с ним, но подобные операции приходится делать несколько сот раз (численное интегрирование), и погрешность со временем накапливается. Может кто сталкивался и нашел решение.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
11.01.2010, 16:18     Вычисления с малыми числами
Посмотрите здесь:

C++ Ошибка при заполнении массива малыми числами
C++ Напишите программу вычисления суммы: 1! + 2! + 3! + … + n!, используя функцию вычисления факториала числа k.
C++ Написать програму вычисления суммы тех элементов целочисленного массива по. n0.n1…n100, которые являются удвоенными нечетными числами
C++ Компьютер неправильно осуществляет вычисления с числами. Как исправить?
C++ Найти все натуральные числа меньше N, которые одновременно являются числами Фибоначчи и простыми числами.
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
insideone
Модератор
Автор FAQ
 Аватар для insideone
3623 / 901 / 48
Регистрация: 10.01.2010
Сообщений: 2,429
11.01.2010, 18:26     Вычисления с малыми числами #2
Возможно дебаггер врет? В реальной программе все так же?

C++
1
2
3
long double x = 15.0E-30L;
long double y = 3.4E-30L;
long double z = x + y + 234.E-30L; // z = 2.5240000000000003e-028
Вроде все нормально - VS 2008

Может если перед сложением домножить оба числа чтобы они стали больше а потом после сложения разделить на него? По крайне мере так же он не должен будет округлять?
Lolcht0
123 / 121 / 5
Регистрация: 30.03.2009
Сообщений: 766
11.01.2010, 18:28     Вычисления с малыми числами #3
нужно пронормировать интеграл, по возможности так, чтобы избежать слишком малых чисел. то есть не чтобы складывать e-12 & e-25, а 1 и е-13
Telliax
0 / 0 / 0
Регистрация: 11.01.2010
Сообщений: 5
11.01.2010, 18:42  [ТС]     Вычисления с малыми числами #4
Цитата Сообщение от insideone Посмотреть сообщение
Возможно дебаггер врет? В реальной программе все так же?

C++
1
2
3
long double x = 15.0E-30L;
long double y = 3.4E-30L;
long double z = x + y + 234.E-30L; // z = 2.5240000000000003e-028
Вроде все нормально - VS 2008

Может если перед сложением домножить оба числа чтобы они стали больше а потом после сложения разделить на него? По крайне мере так же он не должен будет округлять?
Дебаггер не врет, т.к. ведется лог всех вычислений. В твоем примере складываются числа одного порядка, с ними проблем нет. МОжет быть я не четко выразил свою мысль. Проблема возникает когда порядки чисел разлячаются на ~15 и больше, т.е. когда одно число значительно больше другого.
Домножение пробовал - не помогает, да и было бы странно если бы помогло Писал на вс шарпе, потом перешел на КуТэ, проблема никуда не делась.
нужно пронормировать интеграл, по возможности так, чтобы избежать слишком малых чисел. то есть не чтобы складывать e-12 & e-25, а 1 и е-13
Не уверен в том, что это поможет. Проблема не в том, что числа маленькие сами по себе, проблема в том что одно изних сильно меньше другого. Покрайней мере мне так видится
snake32
 Аватар для snake32
1306 / 949 / 120
Регистрация: 26.02.2009
Сообщений: 3,496
Записей в блоге: 5
11.01.2010, 18:48     Вычисления с малыми числами #5
Кто такие комрады?
К сожалению этих ошибок не избежать пока Вы работаете с "плавающей арифметикой".
Нормальных решений в таких проблемах нет. Старайтесь избегать таких малых(или очень больших) чисел. Используйте, тип double вместо float, тк точность там по идеи в 2 раза больше ибо в 2 раза больше памяти отводится под число. В делфях есть тип Extended - 10 байт, скорее всего в С тоже есть такой, я не в курсе.

Как вариант, предложил бы домножить оба числа на 1е12( чтобы точность была чуть выше) и уже с такими числами работать а при выводе обратно на 1е-12.
Если работаете в цикле то лучше, мне кажется, не суммировать слишком малую величину, а использовать умножение, те:
C++
1
2
3
4
5
6
7
8
double step = 1.0e-25;
double s = 0.0;
for(int i=0; i<100; i++)
{
  // что-нибудь делаем с s
  //s+=step; // не так!!! слишком малое значение
  s=step*i;// наверно так лучше будет, но я не уверен - проверь обязательно =)
}
и вообще float-числа очень опасны, тк никогда нельзя точно сказать какая у них погрешность. Почитайте литературу о стр-ре этого типа и сами всё поймёте.

Добавлено через 1 минуту
ох нифига.... пока писал...три поста уже появилось
Telliax
0 / 0 / 0
Регистрация: 11.01.2010
Сообщений: 5
11.01.2010, 19:29  [ТС]     Вычисления с малыми числами #6
Цитата Сообщение от snake32 Посмотреть сообщение
Кто такие комрады?
К сожалению этих ошибок не избежать пока Вы работаете с "плавающей арифметикой".
Нормальных решений в таких проблемах нет. Старайтесь избегать таких малых(или очень больших) чисел. Используйте, тип double вместо float, тк точность там по идеи в 2 раза больше ибо в 2 раза больше памяти отводится под число. В делфях есть тип Extended - 10 байт, скорее всего в С тоже есть такой, я не в курсе.

Как вариант, предложил бы домножить оба числа на 1е12( чтобы точность была чуть выше) и уже с такими числами работать а при выводе обратно на 1е-12.
Если работаете в цикле то лучше, мне кажется, не суммировать слишком малую величину, а использовать умножение, те:
C++
1
2
3
4
5
6
7
8
double step = 1.0e-25;
double s = 0.0;
for(int i=0; i<100; i++)
{
  // что-нибудь делаем с s
  //s+=step; // не так!!! слишком малое значение
  s=step*i;// наверно так лучше будет, но я не уверен - проверь обязательно =)
}
и вообще float-числа очень опасны, тк никогда нельзя точно сказать какая у них погрешность. Почитайте литературу о стр-ре этого типа и сами всё поймёте.

Добавлено через 1 минуту
ох нифига.... пока писал...три поста уже появилось
Я вполне осознаю, что из себя прелставляют вещественные типы. И думаю, что понимаю с чем связана неточность. Что меня интересует, так это способы, позволяющие этой неточности избежать. Но я так понял, что остается только смириться, комрад/коллега/m8/pal/lad/как тебе будет угодно.
Возможно стоит поискать другой численный метод.
Умножение не сработает, т.к. начальное значение не нулевое.
Lolcht0
123 / 121 / 5
Регистрация: 30.03.2009
Сообщений: 766
11.01.2010, 20:23     Вычисления с малыми числами #7
ну поискать другой метод - это конечно лучше всего. можно еще начать считать с другого конца, чтобы сначала складывались маленькие числа, а потом к ним прибавляллись числа побольше.
odip
Эксперт C++
 Аватар для odip
7225 / 3287 / 58
Регистрация: 17.06.2009
Сообщений: 14,165
11.01.2010, 21:32     Вычисления с малыми числами #8
Проблема возникает когда порядки чисел разлячаются на ~15 и больше, т.е. когда одно число значительно больше другого.
Я вполне осознаю, что из себя прелставляют вещественные типы. И думаю, что понимаю с чем связана неточность.
double имеет точность мантиссы 15-16 знаков
То есть если складывать два числа отличающиеся на 15-16 знаков, то есть риск потерять меньшее из этих чисел.

Что меня интересует, так это способы, позволяющие этой неточности избежать.
Использовать long double.
Использовать binary128 http://en.wikipedia.org/wiki/Quadrup...g-point_format
Использовать длинную арифметику - но тут скорость упадет.

Добавлено через 1 минуту
Число знаков мантисы в 10-ичной системе

http://en.wikipedia.org/wiki/Double_...g-point_format
log10(2^53)=15.955

http://en.wikipedia.org/wiki/Quadrup...g-point_format
log10(2^113)=34.016
Telliax
0 / 0 / 0
Регистрация: 11.01.2010
Сообщений: 5
11.01.2010, 21:51  [ТС]     Вычисления с малыми числами #9
Конструктивно, спасибо)
LeBron23
10 / 10 / 1
Регистрация: 18.11.2009
Сообщений: 47
11.01.2010, 22:49     Вычисления с малыми числами #10
Если точность важнее скорости, то самое простое и понятное решение проблемы - длинная арифметика. Можно работать с любым, на которое достаточно памяти числом разрядов и округлять ровно настолько, насколько это необходимо (если необходимо).
Telliax
0 / 0 / 0
Регистрация: 11.01.2010
Сообщений: 5
11.01.2010, 23:18  [ТС]     Вычисления с малыми числами #11
Если точность важнее скорости, то самое простое и понятное решение проблемы - длинная арифметика.
Буду рад линку на алгоритмы/реализацию
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
12.01.2010, 19:48     Вычисления с малыми числами
Еще ссылки по теме:

Определить функцию вычисления площади треугольника по трем его сторонами, заданными действительными числами C++
Вычисления с комплексными числами C++
C++ Строки: заменить в словах цифры малыми буквами латинского алфавита

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
insideone
Модератор
Автор FAQ
 Аватар для insideone
3623 / 901 / 48
Регистрация: 10.01.2010
Сообщений: 2,429
12.01.2010, 19:48     Вычисления с малыми числами #12
http://gmplib.org/
GMP is a free library for arbitrary precision arithmetic, operating on signed integers, rational numbers, and floating point numbers. There is no practical limit to the precision except the ones implied by the available memory in the machine GMP runs on. GMP has a rich set of functions, and the functions have a regular interface.
Выглядит массивно, но по крайне мере алгоритмы можно оттуда подсмотреть.

Добавлено через 18 часов 26 минут
Все таки не сдержался и попытался сделать что то свое, однако ... не зная броду( видимо представление double для различных степеней различно?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
    long double A = 2E-12; const int size = sizeof(A);
    long double B = 2E-25;
    void* vA = &A;
    void* vB = &B; 
    unsigned char* cA = (unsigned char*)vA;
    unsigned char caA[size] = {0};
    unsigned char* cB = (unsigned char*)vB;
    unsigned char caB[size] = {0};
    // Считывание байтов в массивы
    memcpy(&caA[0], &A, size);
    memcpy(&caB[0], &B, size);
 
// Собственно сложение
    unsigned long int Sum = 0; // переменная для суммы и переноса
    // Собственно сложение через массивы
    for (int i = 0; i < !!!; i++)
    {
        // Остаток и сумма текущих байтов имеют один вес, складываем их
        Sum += (caA[i] + caB[i]);
        // Если текущая сумма байтов выше возможной вместимости байта
        if ( Sum > 255 )
        {
            // Оставляем 255 в текущем байте 
            caA[i] = 255;
        }
        else
        {
            // Сумма байт полностью вместилась
            caA[i] = (unsigned char)Sum;
        }
        // Вычитаем их сдвигом, чтобы для старших байт был корректый результат
        Sum = Sum >> 8;
    }
    long double C = 0;
    memcpy(&C, &caA[0], size);
Увеличение младшего байта double увеличивает число, однако такое ощущение что увеличение для A и B разные. В любом случае в оригинальном алгоритме стояло вместо !!! - size и он работал неверно. Если копировать только первый байт (вместо !!! - 2) - заметно что A прибавилось, но больше чем на B... Хотя может что то можно так получить все же?
Yandex
Объявления
12.01.2010, 19:48     Вычисления с малыми числами
Ответ Создать тему
Опции темы

Текущее время: 13:20. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru