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

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
Рейтинг: Рейтинг темы: голосов - 9, средняя оценка - 4.78
Telliax
0 / 0 / 0
Регистрация: 11.01.2010
Сообщений: 5
#1

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

11.01.2010, 16:18. Просмотров 1206. Ответов 11
Метки нет (Все метки)

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

Ошибка при заполнении массива малыми числами - C++
Привет всем! В программе необходимо использовать массив с малыми числами, порядка 1.47243e-331. Элементы массива описаны как "long...

Вычисления с комплексными числами - C++
Собственно вот формула: y2=\frac{-u+v}{2}+i\frac{\sqrt{3}(u-v)}{2} Все данные известны кроме i. Как мне объяснили это комплексное...

Компьютер неправильно осуществляет вычисления с числами. Как исправить? - C++
Создал программу для решения уравнений, синтаксических ошибок нет, но в переменной b компьютер выдает неверное значение. Строка 84. Как это...

Строки: заменить в словах цифры малыми буквами латинского алфавита - C++
Напишите, пожалуйста, программу! Из текстового файла, состоящего из 6-7 строк сформировать массив слов. Заменить в словах цифры малыми...

Определить функцию вычисления площади треугольника по трем его сторонами, заданными действительными числами - C++
Условие: Даны действительные числа a, b, c, d. Найти площадь шестиугольника, изображенного на рис. 4.1. Определить функцию вычисления...

Написать програму вычисления суммы тех элементов целочисленного массива по. n0.n1…n100, которые являются удвоенными нечетными числами - C++
Написать программу вычисления суммы тех элементов целочисленного массива по. n0.n1…n100, которые являются удвоенными нечетными...

Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
insideone
Модератор
Автор FAQ
3638 / 916 / 49
Регистрация: 10.01.2010
Сообщений: 2,469
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
1386 / 1029 / 139
Регистрация: 26.02.2009
Сообщений: 3,820
Записей в блоге: 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
Эксперт С++
7157 / 3297 / 59
Регистрация: 17.06.2009
Сообщений: 14,164
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
Если точность важнее скорости, то самое простое и понятное решение проблемы - длинная арифметика.
Буду рад линку на алгоритмы/реализацию
insideone
Модератор
Автор FAQ
3638 / 916 / 49
Регистрация: 10.01.2010
Сообщений: 2,469
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... Хотя может что то можно так получить все же?
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
12.01.2010, 19:48
Привет! Вот еще темы с ответами:

Найти все натуральные числа меньше N, которые одновременно являются числами Фибоначчи и простыми числами. - C++
Дано натуральное число N. Найти все натуральные числа меньше N, которые одновременно являются числами Фибоначчи и простыми числами. ...

Напишите программу вычисления суммы: 1! + 2! + 3! + … + n!, используя функцию вычисления факториала числа k. - C++
Напишите программу вычисления суммы: 1! + 2! + 3! + … + n!, используя функцию вычисления факториала числа k. И вновь заранее благодарю,...

Составить блок-схему, алгоритм вычисления и программу для вычисления значения кусочно заданной функции - C++
помогите пожалуйста =) заранее благодарен =) П.5.19.Правил Запрещено создавать темы в виде ссылок на задания или коды программ,...

Написать процедуру для вычисления коэффициентов и функцию для вычисления значения многочлена - C++
Задано многочлен {P}_{n}(x) степени n&lt;=100, коэффициенты которого содержатся в действительном массиве A(n+1), и действительные числа...


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

Или воспользуйтесь поиском по форуму:
Yandex
Объявления
12.01.2010, 19:48
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru