рефераты конспекты курсовые дипломные лекции шпоры

Реферат Курсовая Конспект

Особенности вещественных вычислений

Особенности вещественных вычислений - раздел Транспорт, От автора В Отличие От Целочисленных Выражений, Которые Всегда Вычисляются Точно, Вещес...

В отличие от целочисленных выражений, которые всегда вычисляются точно, вещественные выражения дают приближенный результат и вещественные переменные содержат приближенные значения. Это обстоятельство определяется способом хранения вещественных чисел в памяти компьютера. Любое вещественное значение представляется как sm×2p, где s - знак числа (плюс или минус), m - двоичная мантисса, которая принадлежит интервалу [1,2) для всех ненулевых чисел, p - двоичный порядок числа. И мантисса, и порядок хранятся в двоичной кодировке. Величины типа Real занимают в памяти 6 байтов, или 48 битов, старший бит определяет знак числа - если он единичный, то число отрицательное. В следующих 39 битах хранится дробная часть мантиссы, то есть число m-1. И в восьми младших битах хранится число p+129, то есть увеличенный на 129 двоичный порядок числа; такая кодировка выбрана для того, чтобы не хранить отрицательные порядки. Представления чисел типа Single, Double и Extended отличаются по расположению полей от представления типа Real - у них порядок хранится в старших битах, а мантисса - в младших. Способы кодировки этих типов таковы:

тип Single: s (1 бит), p+127 (8 битов),m-1 (23 бита),

тип Double: s (1 бит), p+1023 (11 битов), m-1 (52 бита),

тип Extended: s (1 бит), p+16383 (15 битов), m(63 бита).

Способ кодировки и определяет как допустимые для данного типа значения, так и количество верных десятичных цифр. Определим, например, наибольшее допустимое значение типа Real. Максимальное значение величины p+129, занимающей 8 битов, есть 255, следовательно, наибольший возможный порядок числа равен 126. Наибольшее возможное значение мантиссы чуть меньше двойки, а именно равно 2-2-39, тогда наибольшее число типа Real равно 2127 - 287, что примерно равно 1.7014×1038. Это число называется машинной бесконечностью; конечно, машинная бесконечность своя у каждого вещественного типа. Теперь найдем машинный ноль - наименьшее ненулевое число типа Real. Наименьший возможный порядок числа равен, очевидно, -129 в случае, когда поле порядка содержит число 0, но все вещественные числа, независимо от их мантиссы, у которых поле порядка нулевое, считаются точными нулями, следовательно, машинный ноль имеет порядок -128. Наименьшее значение мантиссы равно единице и, таким образом, машинный ноль типа Real равен 2-128, или примерно 2.9387×10-39. Зная кодировки других вещественных типов, вы легко сможете найти для них машинные бесконечности и машинные нули. По длине поля мантиссы можно оценить количество верных десятичных цифр в значениях данного типа. В типе Real поле мантиссы содержит 39 битов, следовательно, погрешность составляет 2-39, что примерно равно 10-12, таким образом, в числе типа Real не более 12 верных десятичных цифр.

Поработаем немного с внутренним представлением вещественных чисел. Мы знаем, что знак числа во всех вещественных типах определяется одним-единственным битом и этот бит всегда старший. Воспользуемся этим обстоятельством, чтобы написать свою функцию Abs для вещественных аргументов. Если программа откомпилирована в режиме сопроцессора, то можно считать, что аргумент всегда имеет тип Extended, но в противном случае он имеет тип Real и, поскольку эти типы имеют разную длину, вообще говоря, нужны две разные функции. Паскаль позволяет легко справиться с этой проблемой, используя директивы условной компиляции:

 

{$N+,E+}

{$IFOPT N+}

Function Abs(x:Extended):Extended;

{функция Abs для режима с сопроцессором}

Var b : Array[1..10] Of Byte Absolute x;

{совмещаем по памяти 10-байтовый массив с аргументом}

Begin

b[10]:=b[10] And $7F; {зануляем старший бит старшего байта}

Abs:=x;

End;

{$ELSE}

Function Abs(x:Real):Real; {функция Abs для режима без сопроцессора}

Var b : Array[1..6] Of Byte Absolute x;

Begin

b[6]:=b[6] And $7F;

Abs:=x;

End;

{$ENDIF}

Var

r:Real;

d:Double;

s:Single;

Begin

r:=-333;

WriteLn(Abs(r));

d:=-777;

WriteLn(Abs(d));

s:=-555;

WriteLn(Abs(s));

End.

 

Конструкции {$IFOPT N+}, {$ELSE} и {$ENDIF} в этой программе - это директивы условной компиляции. Если опция N была включена, то компилятор будет компилировать операторы от {$IFOPT N+} до {$ELSE} и проигнорирует операторы от {$ELSE} до {$ENDIF}, если же опция N выключена, то компилироваться будет фрагмент программы от {$ELSE} до {$ENDIF}. Таким образом, у нас есть два разных экземпляра функции, но при каждом запуске программы работать будет только один из них, причем именно тот, который нужен. Теперь проверим, верны ли сведения, изложенные в данном разделе, пока мы лишь убедились, что старший бит действительно отвечает за знак числа. Возьмем какое-нибудь вещественное число, например типа Single, и попробуем восстановить его значение по его внутреннему двоичному представлению.

 

Var

x :Single;

b :Array[1..4] Of Byte Absolute x;

p :Integer;

m,Pow :Real;

Mask,L,M_ :LongInt;

k :Byte;

s :Boolean;

Begin

x:=-0.375;

{определим знак числа - он кодируется старшим битом b[4]}

s:=b[4] And $80>0;

If s Then WriteLn('Число отрицательное')

Else WriteLn('Число неотрицательное');

{следующие 8 битов определяют двоичный порядок, увеличенный на 127 - это байт b[4] без старшего бита и старший бит байта b[3]}

p:=Byte(b[4] ShL 1)+b[3] ShR 7;

Dec(p,127);

WriteLn('Двоичный порядок числа=',p);{p=-2}

{оставшиеся 23 бита кодируют дробную часть мантиссы - это байты b[1],b[2] и байт b[3] без старшего бита, соберем их в переменной L}

L:=b[1]+LongInt(b[2])ShL 8+LongInt(b[3] And $7F)ShL 16;

{вычислим мантиссу}

Mask:=1;

M_:=0;

For k:=0 To 22 Do Begin

If L And 1>0 Then Inc(M_,Mask);

Mask:=Mask ShL 1;

L:=L ShR 1;

End;

m:=M_/(LongInt(1)ShL 23);

WriteLn('Дробная часть мантиссы равна ',m);{m=0.5}

m:=m+1;

WriteLn('Мантисса равна ',m);{m=1.5}

{вычислим 2 в степени p}

Pow:=1;

For k:=1 To Abs(p) Do Pow:=Pow*2;

If p<0 Then Pow:=1/Pow;

{теперь вычислим значение переменной x}

x:=m*Pow;

If s Then x:=-x;

WriteLn('Значение переменной равно ',x);{x=0.375}

End.

 

Следствием приближенности вещественных чисел является, например, тот факт, что уравнение x+a=a, которое в обычной математике имеет единственный корень x=0, в компьютерной математике может иметь много нетривиальных решений; если уравнение решается в типе Real, то любой x, удовлетворяющий неравенству x<10-12×a, будет решением. Аналогично тождество (x/a)×a=x, как правило, не выполняется, даже если a не равно нулю. Вообще говоря, операции сравнения “равно” и “не равно” не имеют смысла для вещественных операндов, их следует применять в программах крайне осторожно. Пусть, например, мы хотим вывести значения некоторой функции вещественного аргумента на отрезке [0,1] с шагом 0.1, запишем этот алгоритм так :

x:=0; While x<>1 Do Begin WriteLn(f(x)); x:=x+0.1; End;

и программа зациклится - значение x никогда не станет равным единице. Лучше записать этот фрагмент следующим образом :

For i:=0 To 10 Do Begin x:=i*0.1; WriteLn(f(x)); End;

Однако приближенность представления вещественных чисел имеет не только свои минусы, но и свои плюсы. Решим реальную задачу, в которой используются приближенные вычисления: вычислить сумму ряда åxi/i! , i=0,1,...,∞. Несмотря на то, что необходимо просуммировать бесконечное число слагаемых, эта задача легко решается за конечное время, так как общий член ряда быстро убывает и начиная с некоторого n прибавление очередного слагаемого уже не будет изменять сумму.

 

Var x,S1,S2,a : Real;

Const i : Word = 0;

Begin

Write('Введите x '); Read(x);

a:=1; {общий член ряда}

S2:=0; {предыдущая частичная сумма}

Repeat

S1:=S2;

S2:=S1+a;

Inc(i);

a:=a*x/i;

Until S1=S2;

WriteLn('Сумма ряда = ',S1);

End.

Решим еще одну полезную задачу: найти корень уравнения f(x)=0 методом бисекции. Метод бисекции заключается в следующем: пусть уравнение f(x)=0 имеет единственный корень на отрезке [a,b], это значит, что график функции один раз пересекает ось X на этом отрезке. Определим знак функции в точке a и в точкеx=(a+b)/2. Если эти знаки одинаковы, то очевидно, корень лежит на отрезке [x,b], в противном случае - на отрезке [a,x]. Таким образом, за один шаг метода мы ровно вдвое уменьшили наш отрезок. Будем повторять эти операции до тех пор, пока отрезок не станет очень маленьким, и в качестве корня возьмем середину этого маленького отрезка. Попробуем реализовать этот метод:

 

Const

a : Real=0;

b : Real=10;

Function f(x:Real):Real; Begin f:=exp(x)-2; End;

Const epsilon=1E-10; {"очень маленький" отрезок}

Var x,Fa,Fx : Real;

Begin

Fa:=f(a); {нет необходимости вычислять f(a) в цикле, т.к. ЗНАК функции на левом конце отрезка не изменится!}

While b-a>epsilon Do Begin

x:=(a+b)/2;

Fx:=f(x);

If Fa*Fx<0 Then b:=x Else a:=x;

End;

WriteLn(x);

End.

 

Программа вычислила 0.693147...; легко проверить, что это правильный результат (корень уравнения равен ln2). Казалось бы, мы написали хорошую программу, но давайте теперь вычислим корень уравнения ln(x)-50=0, a=1, b=1e30 : программа зациклится! Выведем внутри цикла значенияa и b : эти числа почти одинаковы (отличие в 12-й цифре) и не меняются, но поскольку их порядок 1021, b-a существенно превосходит наш “очень маленький” отрезок. Есть два способа, которыми мы можем исправить программу. Первый способ заключается в правильном подборе “очень маленького” отрезка, но надо понимать, что вам придется подбирать этот отрезок для каждого нового уравнения, то есть фактически для каждого уравнения писать свою программу. Очевидно, что это бесперспективный путь. Выведем в нашей зацикленной программе не только a и b, но иx, может быть, это поможет нам придумать второй способ: значение x, оказывается, в точности равно b.

Мы могли бы прийти к выводу, что рано или поздно x станет равным илиa, или b, рассуждая чисто теоретически. Действительно, на каждом шаге цикла мы уменьшаем отрезок в два раза, если бы мы работали на вещественной оси, то величины a и bстремились бы друг к другу бесконечно, но, поскольку множество вещественных чисел в компьютере дискретно (из-за конечного количества цифр мантиссы), настанет момент, когда между a и b больше не будет ни одного числа. После этого выражение (a+b)/2 будет давать либо a, либо b. Воспользуемся этим обстоятельством и напишем хорошую программу :

 

Const

a : Real = 1;

b: Real = 1E30;

Function f(x:Real):Real; Begin f:=Ln(x)-50; End;

Var x,Fa,Fx : Real;

Begin

Fa:=f(a); x:=(a+b)/2;

While (x<>a)And(x<>b) Do Begin

Fx:=f(x); If Fa*Fx<0 Then b:=x Else a:=x;

x:=(a+b)/2;

End;

WriteLn(x);

End.

Программа дала верный результат 5.184705...E21.

Сделаем некоторые выводы из вышесказанного. Компьютерные вычисления несколько отличаются от абстрактных математических вычислений. В математике вещественная ось непрерывна (между двумя любыми вещественными числами находится бесконечное множество чисел) - компьютерное множество вещественных чисел дискретно. Математика оперирует с бесконечно большими и бесконечно малыми величинами - компьютерные вещественные числа ограничены сверху и снизу. Математические вычисления точны - компьютерные вычисления приближенны. Вы должны учитывать это, когда программируете какую-либо вычислительную задачу.

– Конец работы –

Эта тема принадлежит разделу:

От автора

B r... Теперь мы можем присвоить переменным их значения...

Если Вам нужно дополнительный материал на эту тему, или Вы не нашли то, что искали, рекомендуем воспользоваться поиском по нашей базе работ: Особенности вещественных вычислений

Что будем делать с полученным материалом:

Если этот материал оказался полезным ля Вас, Вы можете сохранить его на свою страничку в социальных сетях:

Все темы данного раздела:

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

Round(x) - округленное до целого вещественное число, преобразованное к типуLongInt
6. Sqr(x) - квадрат числа 7. Sqrt(x) - квадратный корень 8. Exp(x) - экспонента 9. Ln

Символьный тип данных
Для хранения символьной информации в Паскале предусмотрен специальный тип данных Char. Допустимы переменные, нетипизированные и типизированные константы такого типа. Данные типа

Caseвыражение Of
список значений : оператор/блок .................................. список значений: оператор/блок

Процедуры и функции. Сфера действия описаний
В языке Паскаль (как вы уже поняли из предыдущего материала) существуют понятия процедуры и функции. Процедуры и функции можно определить как замкнут

Открытые массивы и нетипизированные параметры
Из предыдущего раздела мы узнали, что параметры подпрограмм описываются как [Var] имя : имя типа , это правда, но не вся правда - существует еще два

Множества
Понятие множества в Паскале очень близко к математическому определению: множество - это совокупность однотипных неиндексированных объектов. Множества

Графические средства языка Паскаль
Монитор персонального компьютера может работать в двух режимах - текстовом и графическом. Все, что мы делали до сих пор, мы делали в текстовом режиме. Текстовый экран содержит 2000 знако

Case тип Of
константа 1 : (описание поля); константа 2 : (описание поля); .....................

Модуль Crt
Crt - еще один стандартный модуль Паскаля, в котором содержатся разнообразные средства консольного ввода-вывода (то есть ввода с клавиатуры и вывода на текстовый экран). Процедуры

Var TextAttr : Byte
В ней содержится текущий цвет фона и цвет символов, используемые при выводе на экран процедурами Write иWriteLn. Изменив эту переменную, вы задаете новый

Другие средства обработки файлов и модуль DOS
Для того чтобы определить, есть ли на диске файл с заданным именем, удобно использовать уже известную нам стандартную функцию IOResult , которая возвращает ноль при успешном завершении последней оп

Type SearchRec=Record
Fill : Array[1..21] of Byte; Attr : Byte; Time : LongInt; Size : LongInt; Name : Stri

Процедурные типы
Язык Паскаль позволяет использовать в программе данные типа “процедура” или типа “функция”. Такие данные можно передавать как аргументы подпрограмм, можно описывать и использовать массивы процедур

Указатели и динамическая память
Указателями называются переменные и константы, значениями которых являются адреса. Различаются два вида указателей - обобщенные указатели и

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

Открытые строки
  Открытыми строками, или длинными строками, или C-строками, называются символьные последовательности длиной до 65535 символов, ограниченные справа нуль-символ

Использование командной строки и вызов внешних программ
Паскаль позволяет передавать информацию в программу при ее запуске через командную строку. Для этого служат две стандартные функции -ParamCount и ParamStr.

Обработка программных прерываний
Программное прерывание - это ситуация, возникающая, когда дальнейшее выполнение программы невозможно. Например, деление на ноль, переполнение, ошибка Range check error, обращение по неверному адрес

Объекты
Объектом в языке Паскаль называется совокупность данных и подпрограмм, обрабатывающих эти данные. Программирование с использованием объектов называется объектно-о

Type имя типа=Object
описание полей описание методов End; Поля объектов описываются так же, как поля записей, а описание метода - это заголовок процедуры или функции. Сами методы распол

Рекурсия и динамическое программирование
В этом и всех последующих разделах речь пойдет уже не о языке программирования Паскаль, а о задачах, которые вы можете решать с помощью этого языка, о наиболее интересных и полезных алгоритмах и пр

Рекурсия и стек отложенных заданий
Рекурсивные алгоритмы далеко не всегда неэффективны, как можно подумать, прочитав предыдущий раздел. Во многих задачах рекурсивные процедуры и функции очень полезны, кроме того, они исключительно п

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

Комбинаторные алгоритмы
В этом разделе мы рассмотрим три наиболее важные задачи комбинаторики: нахождение всех подмножеств множества из n элементов; нахождение всех выборок по m элементов из n элементов и нахождение всех

Бинарные деревья
В этом разделе мы рассмотрим различные алгоритмы обхода бинарного дерева. К алгоритмам создания бинарного дерева мы обратимся несколько позже, а пока будем считать, что дере

Упорядоченные бинарные деревья и приоритетные очереди
Упорядоченным бинарным деревом, или бинарным деревом поиска, называют дерево, в любой части которого элементы левого поддерева меньше корневого элеме

Алгоритмы сортировки
В этом разделе мы рассмотрим различные алгоритмы решения задачи сортировки. Задача сортировки ставится следующим образом: дана последовательность записей R1,R

Хотите получать на электронную почту самые свежие новости?
Education Insider Sample
Подпишитесь на Нашу рассылку
Наша политика приватности обеспечивает 100% безопасность и анонимность Ваших E-Mail
Реклама
Соответствующий теме материал
  • Похожее
  • Популярное
  • Облако тегов
  • Здесь
  • Временно
  • Пусто
Теги