2013-03-07 3 views
2

Предположим, у меня есть иррациональное число, например \sqrt{3}. Поскольку он иррационален, он не имеет десятичного представления. Поэтому, когда вы пытаетесь выразить это с помощью IEEE 754 double, вы введете ошибку.Могу ли я рассчитать ошибку, введенную двойными?

десятичное представление с большим количеством цифр:

1.7320508075688772935274463415058723669428052538103806280558069794519330169088 
    00037081146186757248575675... 

Теперь, когда я вычислить \sqrt{3}, я получаю 1.732051:

#include <stdio.h> // printf 
#include <math.h> // needed for sqrt 

int main() { 
    double myVar = sqrt (3); 
    printf("as double:\t%f\n", myVar); 
} 

Согласно Wolfram|Alpha, у меня есть ошибка 1.11100... × 10^-7.

Можно ли вычислить ошибку самостоятельно?

(я не против перехода на C++, Python или Java, я мог бы также использовать Mathematica, если нет простой альтернативы.)

Просто уточнить: я не хочу решение, которое работает только для sqrt {3}. Я хотел бы получить функцию, которая дает мне ошибку для любого числа. Если это невозможно, я, по крайней мере, хотел бы знать, как Wolfram | Alpha получает больше ценностей.

Моя попробовать

Во время написания этого вопроса, я нашел это:

#include <stdio.h> // printf 
#include <math.h> // needed for sqrt 
#include <float.h> // needed for higher precision 

int main() { 
    long double r = sqrtl(3.0L); 
    printf("Precision: %d digits; %.*Lg\n",LDBL_DIG,LDBL_DIG,r); 
} 

С этим, я могу получить ошибку до 2.0 * 10^-18 согласно Wolfram|Alpha. Поэтому я подумал, что это может быть достаточно близко, чтобы получить хорошую оценку ошибки. Я написал это:

#include <stdio.h> // printf 
#include <math.h> // needed for sqrt 
#include <float.h> 

int main() { 
    double myVar = sqrt (3); 
    long double r = sqrtl(3.0L); 
    long double error = abs(r-myVar)/r; 
    printf("Double:\t\t%f\n", myVar); 
    printf("Precision:\t%d digits; %.*Lg\n",LDBL_DIG,LDBL_DIG,r); 
    printf("Error:\t\t%.*Lg\n", LDBL_DIG, error); 
} 

Но он выводит:

Double:  1.732051 
Precision: 18 digits; 1.73205080756887729 
Error:  0 

Как я могу исправить это, чтобы получить ошибку?

+0

Итак, вы задаете два вопроса здесь? Как вы вычисляете ошибку в своем ответе, а также что не так со вторым блоком кода? – Mike

+0

@Mike: Да, я задаю два вопроса. Я сделал второй более точный. Поэтому, если на второй ответят, на первый ответ автоматически ответят. Если первый получает ответ, мне не нужен ответ для второго. –

ответ

0

У Вас есть ошибка в печати Double: 1.732051 здесь printf("Double:\t\t%f\n", myVar);

Фактическое значение двойного MYVAR является

1.732050807568877281 //18 digits 

так 1.732050807568877281-1.732050807568877281 равна нулю

+0

[Фактическое значение больше похоже на «1.732050807568877193176604123436845839023590087890625'] (http://ideone.com/1f69Az), я имею в виду, есть еще много цифр. –

+0

@AlexeyFrunze Это действительно не имеет значения ... но ваше значение неверно на 16-й цифре, мое - на 17-м. http://www.wolframalpha.com/input/?i=sqrt+3, так что ... :) есть inf цифры – 2013-03-07 22:24:44

+0

Inf в sqrt (3), но не в переменной с плавающей запятой. –

1

printf патронов удваивается до 6 мест при использовании %f без точности.

например.

double x = 1.3; 
long double y = 1.3L; 
long double err = y - (double) x; 
printf("Error %.20Lf\n", err); 

Мой выход: -0.00000000000000004445

Если результат 0, ваш long double и double одинаковы.

+0

В третьей строке: Почему вы бросаете x, чтобы удвоить? Это двойной, поэтому '(double)' ничего не меняет, не так ли? –

+0

@moose: Просто, чтобы быть откровенным о том, что происходит. – teppic

0

Согласно стандарту C printf("%f", d) по умолчанию будет считаться по 6 цифр после десятичной точки. Это не полная точность вашего двойника.

Возможно, двойные и длинные двойные объекты совпадают с вашей архитектурой. У меня разные размеры для них на моей архитектуре и получить ненулевую ошибку в вашем примере кода.

0

Вы хотите fabsl вместо abs при расчете ошибки, по крайней мере при использовании С (. В C, abs является целым числом) С помощью этой подстановки, я получаю:

Double:  1.732051 
Precision: 18 digits; 1.73205080756887729 
Error:  5.79643049346087304e-17 

(Рассчитано на Mac OS X 10.8.3 с Apple clang 4.0.)

Использование long double для оценки ошибок в double является разумным подходом для нескольких простых вычислений s, за исключением:

  • Если вычисления более точные результаты long double, зачем с double?
  • Ошибочное поведение в последовательностях вычислений сложно описать и может расти до такой степени, что long double не дает точной оценки точного результата.
  • Существуют извращенные ситуации, когда long double получает менее точные результаты, чем double. (В основном встречается, когда кто-то строит пример, чтобы научить студентов урок, но они существуют, тем не менее.)

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

В особых случаях, например при разработке подпрограмм математической библиотеки, ошибки, возникающие в результате определенной последовательности кода, тщательно изучаются (и код изменен, если необходимо, для обеспечения допустимого поведения ошибки). Чаще всего ошибка оценивается либо путем проведения различных «экспериментов», чтобы увидеть, сколько результатов колеблется с переменными входами, либо путем изучения общего математического поведения систем.

Вы также спросили: «Я хотел бы получить функцию, которая дает мне ошибку для любого числа.» Ну, это просто, учитывая любое число х и вычисленный результат х», ошибка в точности x ' - x. Фактическая проблема заключается в том, что у вас, вероятно, нет описания x, который может быть использован для оценки этого выражения. В вашем примере x является sqrt (3). Очевидно, что тогда ошибка представляет собой sqrt (3) - x и x - это точно 1.732050807568877193176604123436845839023590087890625. Теперь все, что вам нужно сделать, это оценить sqrt (3). Другими словами, численная оценка ошибки примерно такая же, как численная оценка исходного числа.

Есть ли какой-то класс чисел, для которого вы хотите выполнить этот анализ?

Кроме того, вы действительно хотите вычислить ошибку или просто хорошую оценку ошибки? Последнее несколько проще, хотя для последовательностей вычислений остается сложным. Для всех элементарных операций IEEE 754 требует, чтобы полученный результат был результатом, ближайшим к математически точному результату (в соответствующем направлении для использования режима округления). В режиме от округления до ближайшей точки это означает, что каждый результат составляет не более 1/2 ULP (единица наименьшей точности) от точного результата. Для таких операций, как те, которые содержатся в стандартной математической библиотеке (синус, логарифм и т. Д.), Большинство библиотек будут давать результаты в нескольких ULP точного результата.

0

Один из способов получения интервала, который гарантированно содержит реальное значение вычисления, заключается в использовании interval arithmetic. Затем, сравнивая результат double с интервалом, вы указываете, насколько далеко вычисляется double, в худшем случае, от реального вычисления.

Анализ стоимости Frama-C может сделать это для вас с опцией -all-rounding-modes.

double Frama_C_sqrt(double x); 

double sqrt(double x) 
{ 
    return Frama_C_sqrt(x); 
} 

double y; 

int main(){ 
    y = sqrt(3.0); 
} 

Анализируя программу:

frama-c -val t.c -float-normal -all-rounding-modes 
[value] Values at end of function main: 
     y ∈ [1.7320508075688772 .. 1.7320508075688774] 

Это означает, что реальная стоимость sqrt(3), и, таким образом, значение, которое будет находиться в переменной y, если программа вычисляется с вещественными числами, находится в пределах double границы [1.7320508075688772 .. 1.7320508075688774].

Анализ стоимости Frama-C не поддерживает тип long double, но если я правильно понял, вы использовали только long double в качестве справки для оценки ошибки, совершенной с помощью double. Недостатком этого метода является то, что long double сам по себе является неточным. С интервальной арифметикой, реализованной в анализе значений Frama-C, реальное значение вычисления гарантировано находится в пределах отображаемых границ.

+0

Я помню математический пакет для C около 25 лет назад, который использовал интервальную арифметику. Интересно, почему эта концепция, похоже, ушла на второй план? – supercat

+0

@supercat В контексте статического анализа популярной и современной задачей является работа над реляционными доменами, где значениям переменных присваиваются не только диапазоны, но и отношения. Как общий метод вычисления ошибок аппроксимации, он, возможно, вышел из моды по тому же разуму: он дает безопасные, но чрезмерно приближенные оценки для выражений, в которых имеются подвыражения, один бесполезный, но типичный пример: «x-x '. –

+0

Конечно, я могу оценить, что существует множество случаев, когда пессимистическая оценка на основе диапазонов даст бесполезные результаты, и я вижу, что «xx» является особенно приятным примером [без возможности гарантировать, что два значения «x» эквивалентны , он расширяется до диапазона (min-max) .. (max-min)].Тем не менее, во многих случаях цель состоит в том, чтобы знать, может ли гарантированный результат быть выше или ниже некоторой ценности. Зная, что весь диапазон выше или весь диапазон ниже, может исключить необходимость в более тщательном анализе. – supercat

Смежные вопросы