Особенности выполнения операций с действительными данными

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

Пусть a=0.523456*102, b=0.746879*102. Тогда

a+b = 1.270335*102» 0.127033*103 (сохранены только 6 разрядов). Возникает погрешность, равная 0.0000005*102. При сложении

a1 = 0.523456*104 и b=0.746879*102 получим 0,53092479*104, или, оставляя 6 значащих цифр, 0.530924*104. Погрешность при выполнении операции составила 0.00000079*104.

При умножении шестиразрядного числа на шестиразрядное получается двенадцатиразрядное число, в котором следует сохранить только старшие 6 разрядов.

Пусть снова a=0.523456*102, b=0.746879*102. Вычислив

a*b = 0.390958293824*104 и оставив 6 разрядов, получим 0.390958*104, т.е. погрешность 0.000000293824*104.

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

2.4.2. Библиотечные математические функции Java

Класс Math содержит функции с плавающей точкой, которые применяются в алгебре, геометрии и тригонометрии (таблицы 2.2-2.4), а также несколько универсальных методов. В Math определены две константы типа double: E (приблизительно 2,72) и PI (приблизительно 3,14). Класс Math находится в пакете java.lang, который импортируется автоматически во все программы.

Таблица 2.2 – Методы трансцендентных функций

Таблица 2.3 – Методы экспоненциальных функций

Таблица 2.4 – Методы округляющих функций

В дополнение к методам, представленным в таблицах 2.2-2.4 в Math определены следующие методы:

Метод IEEEremainder() возвращает остаток от деления dividend/divisor.

Метод random() предназначен для получения псевдослучайного числа из промежутка от 0 до 1.

Метод toRadians() конвертирует градусы в радианы.

Метод toDegrees() переводит радианы в градусы.

2.5. Выполнение работы в лаборатории

2.5.1. Работа в окне кода BlueJ

Внимание! Каждое из перечисленных ниже заданий демонстрирует особенности работы с действительными числами в Java. Листинг (скриншот окна кода) для каждого задания должен быть приведен в отчете по лабораторной работе. Результаты выполнения каждого задания исполнительной системой Java (после их тщательного осмысления!) и соответствующие выводы также должны быть приведены в отчете.

1) Набрать в окне кода double а1=2.523, b1=25.23, c1=0.2523;. Получить значения переменных a1, b1, c1. Сделать вывод.

2) Набрать в окне кода double а2=-523.428e3;. Получить значение a2.

3) Набрать в окне кода double а3=523.428e-3;. Получить значение a3. Сделать вывод по пунктам 2) и 3).

4) Вывести значения переменных a1, b1, c1 в окно терминала в математическом формате при помощи оператора

System.out.printf ("a1=%e; b1=%e; c1= %e \n", a1, b1, c1);.

5) Вывести значения переменных a2 и a3 в окно терминала в математическом формате и фиксированном формате при помощи оператора

System.out.printf ("a2 = %e = %14.6f; а3 = %e = %14.6f \n", a2, a2, a3, a3);

Сделать вывод по пунктам 4) и 5).

6) Ввести в окне кода:

final double M=0.25;

M=М+5;

Какое сообщение будет выдано и почему? Сделать вывод.

7) Набрать в окне кода:

double а4=0.25;

double а5=0.25;

Получить значения переменных a4 и a5, а затем − значение выражения a4==а5. Какого оно типа?

8) Набрать в окне кода:

double a6=5,1;

double a7=a6+0.1;

Получить значение выражения a7==5.2. Получить значение переменной a7. Прокомментировать результат. Сделать вывод по пунктам 7 и 8.

9) Набрать в окне кода:

double a8=25.25;

float a9=25.25f;

Получить значения переменных a8 и a9, а затем − значение выражения a8==a9.

10) Ввести операторы:

a8=a8+0.1;

a9=(float) (a9+0.1f);

Получить значения переменных a8 и a9, а затем − значение выражения a8==a9. Зачем в последнем операторе потребовалось явное преобразование типа? Сделайте выводы по пунктам 9 и 10.

11) Вычислить каждое из трех приведенных ниже выражений дважды: один раз с типом double переменной х, второй раз с типом float переменной x (чтобы не возникла ошибка двойного определения переменной х, во втором случае можно использовать имя x1 вместо x). Для оценки погрешности вычисления сравнить результаты. В качестве значения x взять свой день рождения, деленный на месяц рождения плюс 0.1. Возведение в степень вычислять при помощи операции умножения.

1. х2/2! + x3/3!+x4/4!

Пример:

double x=8.0/11.0;

float x1=(float)(8.0/11.0);

x=x*x/2+x*x*x/(2*3)+x*x*x*x/(2*3*4)

x1=x1*x1/2+x1*x1*x1/(2*3)+x1*x1*x1*x1/(2*3*4)

2. (x-1)/(x+2.5) + ((x-3)/(x-3.5))2

3. (x+2.5*x2-3.7*x3)/(1.34+2.7x3+5.21*x5)

Сделать вывод.

12) Набрать фрагмент кода:

float a=4.0f;

float b=3.5+a;

Какое сообщение выдает система и почему? Исправьте ошибку и получите значение b. Сделайте вывод.

13) Набрать фрагмент кода:

int g=4, h=8;

double z=0.5+g/h;

double q=0.5+4/8;

double s=0.5+4.0/8.0;

Получите значения z, q и s. Объясните результаты. Сделайте вывод.

14) Наберите в окне кода операторы:

System.out.println (Integer.toBinaryString(Float.floatToIntBits (0.9375f)));

System.out.println (Integer.toBinaryString(Float.floatToIntBits (-0.9375f)));

System.out.println (Long.toBinaryString(Double.doubleToLongBits (0.9375)));

System.out.println (Long.toBinaryString(Double.doubleToLongBits (-0.9375)));

Объясните результат. Сравните с представлением чисел 0.9375 и -0.9375 в формате IEEE-754 для типов float и double, полученным вручную. Представление числа 0.9375 в формате IEEE-754 для типа float приведено на рисунке 2.3. Сделайте выводы.

2.5.2. Разработка программы для выполнения операций с вещественными числами

1) Разработайте программу согласно варианту задания (таблица 2.5).

2) Обоснуйте выбор типа для вещественных переменных.

3) Проведите отладку программы и испытание на заданных тестовых примерах.

2.6. Варианты заданий

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

Вариант задания V необходимо вычислить по формуле V=N%14,

где N – номер студента в списке группы.

Для решения поставленной задачи выполните следующие этапы.

1) Преобразуйте формулы с целью уменьшения количества операций при вычислениях. Упрощение возможно как за счет математических преобразований, так и за счет введения дополнительных переменных для сохранения значений выражений, неоднократно встречающихся в формуле.

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

3) Составьте алгоритм (схему) программы.

4) В соответствии с алгоритмом составьте программу на языке Java.

5) Запустите программу на выполнение при обоих вариантах значений входных переменных. Проанализируйте полученные результаты (сравните с результатами расчетов на калькуляторе) и сообщения исполнительной среды Java.

Таблица 2.5 – Варианты заданий

2.7. Пример выполнения индивидуального задания

Постановка задачи: Вычислить значение а по формуле (2.1)

(2.1)

для x = 1,241, y = –0,879.

Проанализируем формулу (1.4) с целью выявления возможности упростить вычисления. Знаменатель первой дроби можно упростить следующим образом:

(x +1)+(x -1)=2 x. (2.2)

Чтобы дважды не вычислять | y |, введем дополнительную переменную

. (2.3)

Тогда с учетом соотношений (2.2), (2.3) формулу для вычисления значения

переменной a можно переписать следующим образом:

.

Алгоритм вычислений будет иметь вид, показанный на рисунке 2.5.

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

Рисунок 2.5 – Схема алгоритма вычислений

Текст программы на языке Java:

public class Lab5_1{

public static void main (String args []){

double x,y,b,a;

x=1.241;

y=-0.879;

b=Math.abs(y);

a=Math.pow(b,1.0/3.0)/(2*x)+Math.abs(Math.sin(x))/Math.pow(b,1.0/x);

System.out.printf("x=%8.4f; y=%8.4f; a=%8.4f \n",x,y,a);

System.out.println("Значение переменной а в формате IEEE 754: ");

System.out.println(Long.toBinaryString(Double.doubleToLongBits (a)));

}

}

Результаты выполнения программы приведены на рисунке 2.6.

Рисунок 2.6 – Результаты вычислений

Значение переменной a, полученное в результате выполнения программы, совпало со значением, рассчитанным при помощи калькулятора.

Внимание! Если в программе вы получили значение, не совпадающее с вашими расчетами (а вы точно уверены, что посчитали на калькуляторе правильно), в процессе отладки программы рекомендуется выводить в окно терминала все промежуточные результаты вычислений для обнаружения логической ошибки в программе. Например, при отладке приведенной выше программы была обнаружена следующая логическая ошибка: выражение для вычисления значения a первоначально выглядело так:

Math.pow(b,1.0/3.0)/(2*x)+Math.abs(Math.sin(x))/Math.pow(b, x)

вместо

Math.pow(b,1.0/3.0)/(2*x)+Math.abs(Math.sin(x))/Math.pow(b,1.0/x), что и давало неправильный результат.

2.8. Рекомендации по составлению отчета по лабораторной работе

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

В дополнение к общим требованиям к отчету, представленным в разделе 1, тщательно опишите все исследования, которые вы провели в окне кода (п. 2.5.1) и сделанные вами выводы.

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

2.9. Контрольные вопросы

1) Какие типы Java определяют вещественные числа?

2) Какой формат используется для внутреннего представления вещественных данных? Из каких полей состоит этот формат.

3) Какое вещественное число считается нормализованным в математике, а какое в формате IEEE-754?

4) От чего зависит точность представления вещественных чисел в ЭВМ? От чего зависит диапазон представления вещественных чисел в ЭВМ?

5) Какой из форматов обеспечивает большую точность и диапазон и почему?

6) Как в программе задать литерал типа float?

7) Как задать литерал типа double?

8) Как выглядит действительное число, заданное в естественной форме (с фиксированным числом знаков после запятой) и в научной форме?

9) Как определить вещественную переменную в программе? Чем нужно руководствоваться при выборе ее типа?

10) Как определить вещественную константу в программе?

11) Когда в Java-программе может возникнуть ошибка, связанная с потерей точности? Приведите пример. Для чего используется явное преобразование типа в выражениях?

12) Объясните, почему возникает и накапливается погрешность при выполнении машинных расчетов с действительными данными. Как это влияет, в частности, на результат операции сравнения на равенство (==) действительных переменных.

13) Какой тип с плавающей точкой обеспечивает меньшую погрешность и за счет чего?

14) Какие библиотечные методы вы применили для получения текстового эквивалента представления вещественного числа в форматах IEEE-754 с одинарной и двойной точностью. Какие оговорки нужно сделать по-поводу полученного представления положительного числа? В каких классах-оболочках находятся использованные методы?

15) В каком библиотечном классе определены методы для вычисления математических функций? Нужно ли импортировать в программу этот класс? Какие функции вы использовали в своей программе?

3. ЛАБОРАТОРНАЯ РАБОТА № 6. ОБРАБОТКА ДАННЫХ ТИПА CHAR И BOOLEAN. УСЛОВНЫЕ ВЫРАЖЕНИЯ. ТРОИЧНАЯ УСЛОВНАЯ ОПЕРАЦИЯ JAVA

3.1. Цель работы

Освоить работу с типами boolean и char, научиться применять методы класса Character для анализа символов, научиться составлять сложные условные выражения, изучить синтаксис и научиться применять троичную условную операцию Java.

3.2. Постановка задачи

1) Ознакомиться с принципами хранения и обработки символов и логических значений в Java.

2) Выполнить заданные операции над данными типа char в окне кода.

3) Выполнить заданные операции над данными типа boolean в окне кода.

4) Разработать и отладить программу, демонстрирующую использование типов char и boolean, условных выражений, а также троичной условной операции.

3.3. Внеаудиторная подготовка

Для подготовки к лабораторной работе следует ознакомиться с [1] (П.Ноутон, Г.Шилдт. Java 2, с.48-50, 52-64).

3.4. Краткие теоретические сведения

3.4.1. Тип char

Тип char представляет коды символов Unicode. Код символа – это номер соответствующего символа в таблице Unicode. Код является 16-битовым целым значением (без знака). Дополнительные сведения о символах Unicode можно получить, обратившись к Интернет-ресурсам:

http://www.unicode.org/charts/PDF,

ru.wikipedia.org/wikisagesign.ru./tools/Unicode,

http://www.javaportal.ru/java/articles/ruschars/ruschars.html и др.

Число символов, которые можно закодировать с помощью 16 битов равно 216=65536 (диапазон значений кода символа ─ от 0 до 65535).

Преимущество Java: набор символов Unicode определяет полный набор интернациональных символов, который может представлять все символы, находящиеся во всех человеческих языках. Язык java cсоздан для разработки апплетов «всемирного использования»!

Недостаток: Использование Unicode несколько неэффективно для языков, подобных английскому, немецкому, французскому, чьи символы могут легко помещаться в 8 битах. (плата за «глобальную мобильность»).

Стандартный набор символов, известный как ASCII (включает буквы английского алфавита, цифры, символы пунктуации, специальные символы) располагается в интервале значений кодов от 0 до 127 (как обычно), а расширенный восьмиразрядный набор символов ISO-Latin-1 ─ в диапазоне от 0 до 255.

Фрагмент кодовой таблицы, содержащий кириллические символы приведен на рисунке 3.1.

Рисунок 3.1. – Фрагмент таблицы Unicod с кириллическими

символами (www.unicode.org)

Тип char в Java по-существу является подмножеством типа int, поэтому для типа char определены целочисленные операции, например, для данных типа char допустимо действие код±целое. В программе программист действует с символами, а компилятор преобразует эти действия в работу с кодами. Следует обратить внимание, что при вычисления выражения, в котором используются целочисленные операции над символами, их коды преобразуются в значения типа int. Соответственно тип значения такого выражения – int. Если это значение присваивается переменной типа char, нужно использовать явное преобразование типа, например,

char ch=(char)('A'+'B');.

Операции сравнения символов на самом деле сравнивают их коды.

В программе можно использовать переменные и литералы типа char.

Литерал типа char – это символ, заключенный в одиночные кавычки: 'z', '7', '+', '&', 'ю', 'Я' и т.д.

Символ можно задать также его кодом (целым числом в десятичной системе счисления), например char ch=88;

Некоторые «символы» не имеют графического изображения. Такие символы кодируются особо, например, '\n' – новая строка, ′\r′ – возврат каретки, ′\t′ – табуляция. Некоторые символы имеют специальный смысл – одиночные и двойные кавычки, слэш. Литералы этих символов записываются при помощи символа \: одиночная кавычка – '\′', двойная кавычка '\"', слэш – '\\'. Можно задать восьмеричный или шестнадцатеричный код символа: '\aaa' (не более трех восьмеричных цифр а) или '\uxxxx' (ровно четыре шестнадцатеричных цифры х). Такой способ задания называется escape-последовательностью. Основные escape-последовательности приведены в таблице 3.1.

Таблица 3.1 – Основные escape-последовательности

В практике программирования символы принято делить на категории: буквы, цифры, управляющие символы, пробельные символы и другие. Различают также большие и маленькие буквы. Проверка того, что символ принадлежит определенной категории, предусмотрена в классе-оболочке Character. Так, Character.isDigit(x) возвращает true, если x – цифровой символ. Метод isLetter (x) проверяет, буква ли x. Имеются методы isLetterOrDigit(x) (х – буква или цифра?), isLowerCase(x) (x – маленькая буква?), isUpperCase(x) (x – большая буква?), isSpaceChar(x) (x – пробел?).

Метод Character.toUpperCase(x) возвращает большую букву, соответствующую букве − значению х (проверьте в панели кода: Character.toUpperCase('ж')). Метод Character.toLowerCase(x) сопоставляет маленькую букву, соответствующую букве − значению х.

3.4.2. Тип boolean

Булевский тип в Java – это простой тип boolean и класс-оболочка Boolean. Литералы типа boolean – это false и true («ложь» и «истина»). К булевскому типу применимы операции сравнения (== и !=) и операции булевой логики (таблица 3.2).

Таблица 3.2 – Операции булевой логики

Значения булевского типа получаются как результат арифметического сравнения значений других типов и/или как результат операций И (AND − &), ИЛИ (OR − |), Исключающее ИЛИ (XOR − ^), НЕ (NOT −!). Таблица 3.3 демонстрирует правила выполнения этих операций (аналогичны действиям этих операций на битах целых чисел).

Таблица 3.3 – Правила выполнения операций булевой логики

Короткие логические операции (&& и ||).

Как видно из таблицы 3.4, односимвольная операция ИЛИ приводит к true-результату, когда операнд А – true, независимо от того, каков B. Точно также операция И приводит к false-результату, когда А – false, независимо от того, каков В.

Если использовать двузначные формы (&& и ||) вместо однозначных (& и |), Java вообще не будет выполнять оценку правого операнда, если значение выражения полностью определяется значением левого операнда.

Использование короткой формы стало стандартной практикой в булевской логике, оставляя версии с одиночным символом исключительно для поразрядных операций целыми типами (хотя есть и исключения из этого правила).

Типовое применение булевского типа и операций с ним – хранение результатов проверки сложных условий. Например, (x>2.0)&(x<5.0) будет иметь значение true, если действительное x удовлетворяет неравенству 2.0<x<5.0. Выражение!(x>2.0) истинно для тех же x, что и выражение (x ≤ 2.0).

Результаты булевских операций с константами и булевской переменной x:!true → false,!false → true, x|true → true, x|false → x, x&false → false, x&true → x.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: