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

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

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

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

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

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

Ошибка при заполнении массива малыми числами - 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
3636 / 914 / 49
Регистрация: 10.01.2010
Сообщений: 2,464
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
1376 / 1019 / 136
Регистрация: 26.02.2009
Сообщений: 3,766
Записей в блоге: 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
Эксперт С++
7155 / 3295 / 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
Если точность важнее скорости, то самое простое и понятное решение проблемы - длинная арифметика.
Буду рад линку на алгоритмы/реализацию
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
12.01.2010, 19:48     Вычисления с малыми числами
Еще ссылки по теме:

работа с числами - C++
a) Дано трёхзначное число. Определить, есть ли среди его цифр одинаковые? b) Дано натуральное число с различными цифрами. Определить,...

Работа с числами - C++
ну вопщем у меня есть решение задач на турбо паскале а мне их надо решить в С++...я решил, принес преподавателю а мне сказали, что надо без...

Операции с числами - C++
Даны целое число k(1&lt;k&lt;180) и последовательность цифр 10111213...9899, в которой выписаны подряд все двузначные числа. Определить: ...

работа с числами - C++
мне интересует вот что. Например я ввожу число 1234, программа должна выводить 1 2 3 4,то есть разделить число а каждую цифру засунуть в...


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

Или воспользуйтесь поиском по форуму:
insideone
Модератор
Автор FAQ
3636 / 914 / 49
Регистрация: 10.01.2010
Сообщений: 2,464
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     Вычисления с малыми числами
Ответ Создать тему
Опции темы

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