Под потоком с проверкой арифметики с плавающей запятой

Я программирую библиотеку с несколькими экземплярами длинных математических формул, которые иногда теряют значение при использовании двойных значений. Одним из примеров может быть:

(Exp(-a*a) - Exp(-b*b))*Exp(c)*Exp(d) 

И a, b, c, d также включают некоторые вычисления аналогичного типа. Я могу иметь дело с двойниками, получающими это неправильно (и возвращать соответствующее сообщение об ошибке или некоторую аналитическую границу), но если я не обнаруживаю недополнение (например, по разнице экспонент), это приводит к поведению, которое я не могу себе позволить. (Как абсолютные, так и относительные ошибки могут быть огромными, когда эта разница достигает нуля, а другие экспоненты очень велики).

Есть ли что-то похожее на ключевое слово checked, которое работает для двойников? Есть ли способ, которым я могу реализовать проверки с помощью?

Любое решение, которое гарантирует его правильность, даже то, которое поднимает больше флагов, чем необходимо, подходит для меня.


Этот вопрос был предложен как дубликат, но «ручная проверка перед каждым умножением» не особенно полезно для меня.


person Radost    schedule 24.06.2019    source источник
comment
Возможный дубликат Как обнаружить полную потерю точности с помощью Двойники?   -  person O. Jones    schedule 25.06.2019
comment
@O.Jones В каком-то смысле дубликат, но реального решения там не было. Проверка буквально каждого умножения с помощью непроцедурно сгенерированного кода возможна, но я бы предпочел этого избежать по очевидным причинам.   -  person Radost    schedule 25.06.2019
comment
C# не создает исключений для плавающего потери значимости. Он просто устанавливает результат равным нулю. Итак, вы застряли, выполняя свою собственную эпсилон-работу. Это боль в шее. Как вы обнаружили.   -  person O. Jones    schedule 25.06.2019
comment
Если производительность и время разработки не являются проблемой, вы можете определить свой собственный класс, содержащий оператор с плавающей запятой и перегрузки * и другие операции (путем выполнения проверок). Таким образом, ваш код будет таким же чистым, как и раньше, но это требует некоторой работы.   -  person    schedule 25.06.2019


Ответы (1)


Есть ли что-то похожее на проверенное ключевое слово, которое работает для двойников?

Неа.

Есть ли способ, которым я могу реализовать проверки с помощью?

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

Лучшее решение: вы можете подумать о написании библиотеки символьной логики. Рассмотрим, например, что произойдет, если вы создадите свой собственный тип числа:

struct ExpNumber 
{
  public double Exponent { get; }
  public ExpNumber(double e) => Exponent = e;
  public static ExpNumber operator *(ExpNumber x1, ExpNumber x2) => 
    new ExpNumber(x1.Exponent + x2.Exponent);

И так далее. Вы можете определить свое собственное сложение, вычитание, степени, логарифмы и т. д., используя известные вам тождества для степеней. Затем, когда пришло время реализовать это обратно к двойному, вы можете реализовать это, используя любой стабильный алгоритм, который избегает потери значимости, который вы предпочитаете.

Проблема в том, что двойники намеренно обменивают снижение репрезентативной мощности и точности на значительное увеличение скорости. Если вам нужно точно представлять числа меньше 10e-200, удвоение не для вас; они были разработаны для решения задач в физических вычислениях, а таких малых физических величин не существует.

person Eric Lippert    schedule 24.06.2019