Задать вопрос

Как обойти ограничение точности типа double в C#?

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

x1 = 0.00000000000000030426102110965129;
x2 = 0.00000000000000030426102110965134;
Получаем x = 0.00000000000000030426102110965134

x1 = -0.000000000000000070906981940269862;
x2 = -0.000000000000000070906981940269849;
Получаем x = -0.000000000000000070906981940269862

Пытался преобразовывать значащие цифры в long получить (x1+x2) / 2 и преобразовывать обратно в double. Однако такой способ тоже в какие-то определенные моменты приводит к ошибкам + медленный (за счет вычисление степени числа). Может быть кто-нибудь знает как это проще выполнить?

2. Производится вычисление массива чисел. Каждый последующий элемент массива определенным образом зависит от всех предыдущих. В результате значения растут достаточно быстро с увеличением длины массива, и выходит за рамки ±1.7×10E308. В дальнейшем производится нормировка данного массива, т.е. A[i] / summa (A[i]^2, i=0..N-1) . Результирующий массив в рамки double, вообще говоря, помещается.
Есть какие-нибудь простые способы решения данной проблемы? Пока приходится использовать собственную структуру, представляющую из себя два числа int (степень) и double (значение от ~0.99 до ~9.99). Однако это очень пагубно влияет на общую производительность и, к тому же, крайне не удобно.
Так же быть может, кто-то знает простой способ получения значащих цифр и степени double? Ибо приходится либо циклом уменьшать/увеличивать число десятками и считать степень, либо использовать преобразование числа в строку, с последующим разбиением ее по 'E' и парсингом, что при этом, не исключает надобности первого варианта.

UPD:
В результате тестирования и обсуждения выяснилось следующее:
2. Решается только через созданный новый тип.
1. Собственный тип не поможет. При обратной конвертации все вернется назад. Но откуда растут ноги проблемы выяснить удалось.
Дело в том, что эти числа в двоичной записи различаются всего на 1 бит. И хоть ты тресни, но среднего значения никогда не получишь.
Пример:

x1 = 0.00000000000000030426102110965129;
Получив массив байт значения имеем:
119 95 82 12 160 236 181 60
x2 = 0.00000000000000030426102110965134;
Аналогично:
120 95 82 12 160 236 181 60

Что делать в такой ситуации абсолютно не понятно.
  • Вопрос задан
  • 6130 просмотров
Подписаться 4 Оценить Комментировать
Пригласить эксперта
Ответы на вопрос 5
Deerenaros
@Deerenaros
Программист, математик, задрот и даже чуть инженер
Так. Стойте.

Decimal может принимать огромный диапазон значений, но из-за преобразований десятичную в двоичную систему исчисления точности всё равно может не хватить.

Как вариант, можно использовать BigInteger и хранить запятую отдельным числом, можно тоже BigInteger. По сути - запятая это показатель степени при основании системы исчисления, то есть 10. Арифметика элементарная.

Есть тысячи реализаций (включая BigRational в C# F#).

Загружаться они почти все могут из строки, а те кто не могут, сплитим по точке и считаем количество нулей. Но должен предупредить - данный метод может зациклиться и очень на долго.
Ответ написан
@carbon88
.NET developer/ORM developer
1) Есть тип Decimal. Можете попробовать его. У него точность побольше будет чем у double. Если нужно еще больше, то вам уже нужно реализовывать математику больших чисел.
Ответ написан
morozovdenis
@morozovdenis
создайте свой тип данных комплексный

пусть хранит два числа double(число например 1.5) и int(число 9 например, что означает 1.5/(10^9))

т.е. что (1.5, 9) == 0.0000000015

делите, складывайте и т.д. их
Ответ написан
Killy
@Killy
2) ideone.com/YjX5k6 или stackoverflow.com/questions/389993/extracting-mant...

1) Можно ли модифицировать алгоритм так, чтобы вычисления проводились в условных координатах от 0.0 до 1.0 (например), вместо реальных x1 .. x2, и затем вычислить реальное x любым медленным способом?
Ответ написан
BigObfuscator
@BigObfuscator
Что делать в такой ситуации абсолютно не понятно.

А вы преобразуйте уравнение так, что бы плохо обусловленный участок стал хорошо обусловленным (кстати это называется регуляризацией).
Например по формуле x' = 1/(x - x1)
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы