Математические операции
Базовые арифметические операции, которые предоставлены языком С – сложение, вычитание, умножение и деление – обозначаются символами +, -, * и / соответственно. В основном они работают так, как и можно было ожидать, за исключением деления, в поведении которого, существуют некоторые отклонения, когда оно применяется к целым или константам.
+ сложение (Операция сложения «+» приводит к сложению двух значений, размещенных слева и справ от знака сложения.)
- вычитание (Операция вычитания «-» приводит к вычитанию числа, следующего за знаком «–», из числа, предшествующего этому знаку)
* умножение (Вычисляет произведение двух операндов)
Операнды, над которыми выполняется операции, могут быть константами или переменными
/ деление (Величина, расположенная слева от знака деления «/», делится на значение, указанное справа от этого знака. Например, в результате выполнения оператора b=45.0/9.0; переменная b получит значение 5.0. Здесь выполняется деление над данными с плавающей точкой, и результат также является числом с плавающей точкой. При выполнении операции деления над операндами смешанных типов результат также будет являться числом с плавающей точкой, например: c = 36 /12.0; Результатом операции деления будет число с плавающей точкой 3.0. При делении целых дробная часть результата отбрасывается.
% получение остатка от деления нацело (деление по модулю). Операцию % нельзя использовать с типами FLOAT или DOUBLE, она применяется только к данным целого типа. Операция X % Y дает остаток от деления X на Y и, следовательно, равно нулю, когда X делится на Y точно.
Бинарными арифметическими операциями являются +, -, *, / и операция деления по модулю %. Имеется унарные операции -, и +. Унарный + не изменяет значение своего операнда.
Унарный знак меняет знак своего единственного операнда на противоположный. Пример применения:
int a = 0;
int b = -5;
а = -b; // минус изменяет знак операнда
Здесь а будет присвоено значение +5, поскольку унарный минус изменил знак операнда b.
Очень часто операции с операндами группируются в выражения. Операции в выражении выполняются в порядке приоритета, причем некоторые операции выполняются справа налево, а некоторые наоборот. Приоритет представляет собой основное правило определения порядка вычисления выражений.
Рассмотрим приоритеты математических операций.
Операции + и - имеют одинаковое старшинство, которое младше одинакового уровня старшинства операций *, / и %, которые в свою очередь младше унарного минуса. Арифметические операции группируются слева направо. Приоритет операции можно повысить если применить () – они имеет самый высокий приоритет. Операция = имеет самый низкий приоритет (выполняется последней) и выполняется справа налево.
Например, в нижеприведенных выражениях цифрами показана последовательность выполнения операций
a * b – c / d * f + g + h
1 4 2 3 5 6
При этом скобки меняют последовательность выполнения операций:
a * b – ( c / (d * f ) + g )+ h
4 5 2 1 3 6
К математическим операциям относят и две необычные операции для увеличения и уменьшения значений переменных. Операция увеличения ++ (инкремент) добавляет 1 к своему операнду, а операция уменьшения – (декремент) вычитает 1. Это унарные операции.
Необычный аспект заключается в том, что ++ и -- можно использовать либо как префиксные операции (перед переменной, как в ++n), либо как постфиксные (после переменной: n++). Эффект в обоих случаях состоит в увеличении n. Но выражение ++n увеличивает переменную n до использования ее значения, в то время как n++ увеличивает переменную n после того, как ее значение было использовано. Это означает, что в контексте, где используется значение переменной, а не только эффект увеличения, использование ++n и n++ приводит к разным результатам. Если n = 5, то х = n++; устанавливает х равным 5, а х = ++n; полагает х равным 6. В обоих случаях n становится равным 6.
Операции увеличения и уменьшения можно применять только к переменным; выражения типа х=(i+j)++ являются незаконными. В случаях, где нужен только эффект увеличения, а само значение не используется, как, например, в n++; выбор префиксной или постфиксной операции является делом вкуса, но встречаются ситуации, где нужно использовать именно ту или другую операцию.
Следующий пример позволяет проиллюстрировать использование математических операций.
#include <iostream>
#include <math.h>
using namespace std;
int main(void)
{ setlocale(NULL,"Russian");
int summa,r50,o50,r10,o10,r5,o5,r1;
cout<<"Введите сколько надо сдачи в рублях";
cin>>summa;
r50=summa/50; //расчет сколько нужно банкнот достоинством 50 рублей
o50=summa%50; // сколько ослалось денег, если выдать все 50 руб. банкноты
r10=o50/10; //сколько нужно 10 рублевых банкнот на остаток
o10=o50%10; // сколько ослалось денег, если выдать все 50 и 10 руб. банкноты
r5=o10/5; //сколько нужно 5 руб/ манет на остаток
o5=o10%5; // сколько ослалось денег
r1=o5;
cout<<" Чтобы дать "<<summa<<" рублей сдачи, используйте:\n";
cout<<r50<<" банкноты достоинством 50 рублей\n ";
cout<<r10<<" банкноты достоинством 10 рублей\n ";
cout<<r5<<" монеты достоинством 5 рублей \n";
cout<<r1<<" монеты достоинством 1 рублей \n";
return 1;
}
Результат работы программы выглядит следующим образом (рис. 23).
Рис. 23. Результат работы программы, иллюстрирующей использование
математических операций
Функционал языка С не ограничивается только возможными операциями. В MS VS 2008 существует довольно обширная библиотека стандартных функций, которую, может использовать программист-разработчик. Функции из этой библиотеки называются стандартными (написанными разработчиками С, а не программистом) и они группируются в различных библиотечных файлах (двоичные файлы с расширением .lib) по месту применения. Для использования стандартных функций необходимо подключить заголовочный файл для библиотеки, где расположена используемая функция. В предыдущих главах уже применялась библиотека функций ввода-вывода для работы с потоками iostream. Математические функции относятся к математической библиотеке, и заголовочный файл для работы с ними называется math.h. В этом файле определены прототипы математических функций, а также некоторые математические константы.
Математическими называют функции, с помощью которых вычисляются тригонометрические, логарифмические, экспоненциальные и другие функции.
В таблице 4 приведены наиболее часто используемые математические функции. В первом столбце представлена строка прототипа функции, которая содержит следующую информацию:
Тип_результата имя_функции (тип_параметра имя_параметра);
Тип_результата – это значение функции которое она должна сосчитать, имя_функции – название функции, в круглых скобках размещается список формальных параметров, через , (если их несколько) ставятся типы переменных и имена переменных, необходимых для расчета значений функции.
Таблица 4
Математические функции
Функция | Назначение функции |
double sin( double X); | синус x |
double cos(double X); | косинус x |
double tan( double X); | тангенс x |
double asindouble X); | арксинус x |
double cosh( double X); | арккосинус x |
double atandouble X); | арктангес x |
double exp( double X); | экспоненциальная функция еx |
double log( double X); | натуральный логарифм ln(x), x>0 |
double log10( double X); | десятичный логарифм lg(x), x>0 |
double pow( double X, double Y); | х в степени у |
double sqrt( double X); | квадратный корень от х |
double fabs(double X); | модуль аргумента x |
double abs( double X); int abs(int X); | |
double fmod( double X, double Y); | остаток от / для типов double |
double ceil(double X); | округление в меньшую сторону |
double floor(double X); | округление в большую сторону |
Следует помнить, что тригонометрические функции рассчитываются в радианах, а не в градусах, поэтому следует знать форму перевода градусов в радианы, если необходимо использовать более привычные меры. . Функции обратные к тригонометрическим возвращают радианы. В языке С нет функции для расчета
. Для расчета логарифма по свободному основанию необходимо использовать одну из формул приведения
Для вызова стандартных функций используется оператор вызова функции, состоящий из ее имени и списка фактических параметров. Например: для того, что бы получить значение x4 , необходимо написать в программе pow(x,4). Этот код может стоять справа от оператора присваивания, использоваться в вычисляемом выражении или быть размещен в операторе вывода на экран.
При использовании степенной функции у неопытного пользователя часто возникает ошибка (рис. 24).
Рис. 24. Сообщение компилятора об ошибке при использовании степенной фукции
Это связано с тем, что разработчики M S V S 2008 определили, что функция pow() может работать со следующими наборами параметров:
double pow(double x, double y);
double pow(double x, int y);
float pow(float x, float y);
float pow(float x, int y);
long double pow(long double x, long double y);
long double pow(long double x, int y).
Компилятор С в M S VS 2008 не всегда может однозначно выбрать приведение к одному из шести вариантов в случае, если параметры функции по типу отличаются от приведенных наборов. Поэтому и возникает такая ошибка. Чтобы от нее избавится, достаточно принудительно привести параметры к полному соответствию с одним из приведенным выше списков.
В M S VS 2008 при работе с математическими константами необходимо в код программы добавить строку:
#define _USE_MATH_DEFINES
перед подключением заголовочного файла math.h.
Это строка определяет макрос, от наличия которого зависит будет подключен блок с определением математических констант заголовочного файла или нет. Если строка в код программы добавлена, то можно использовать следующие математические константы (табл. 5).
Таблица 5
Математические константы
Название | Значение | Описание |
M_E | 2.71828182845904523536 | e |
M_LOG2E | 1.44269504088896340736 | log2(e) |
M_LOG10E | 0.434294481903251827651 | log10(e) |
M_LN2 | 0.693147180559945309417 | ln(2) |
M_LN10 | 2.30258509299404568402 | ln(10) |
M_PI | 3.14159265358979323846 | p |
M_PI_2 | 1.57079632679489661923 | p/2 |
M_PI_4 | 0.785398163397448309616 | p/4 |
M_1_PI | 0.318309886183790671538 | 1/p |
M_2_PI | 0.636619772367581343076 | 2/p |
M_2_SQRTPI | 1.12837916709551257390 | 2/sqrt(p) |
M_SQRT2 | 1.41421356237309504880 | sqrt(2) |
M_SQRT1_2 | 0.707106781186547524401 | 1/sqrt(2) |
Рассмотрим пример использования математических операций.
Программа выводит на экран результаты вычислений следующих математических выражений:
,
,
, и
.
#include <iostream>
#define _USE_MATH_DEFINES //макрос для работы с константами
#include <math.h>// подключение математических функций
using namespace std;
int main(void)
{
setlocale(NULL,"Russian");// подключение кодировочной страницы
double x,y,z;
cout<<"Введите начальные значения x,y : ";
cin>>x>>y;
z=log(x+sqrt(1+x*x));
cout<<" 1 –ое выражение log("<<x<<"+sqrt(1+"<<x<<"^2))="<<z<<endl;
z=2.*cos(x-M_PI/6.)/(1./2.+pow(sin(y),2.));
cout<<" 2 - ое выражение 2.*cos("<<x<<"-M_PI/6.)/(1./2.+pow(sin("<<y<<"),2.))="<<z<<endl;
z=(1-y)*(x+y/(x*x+4))/(exp(-x-2)+1./(x*x+4));
cout<<" 3 - е выражение (1-"<<y<<")*("<<x<<"+"<<y<<"/("<<x<<"^2+4))/(exp(-"<<x<<"-2.)+1./"<<x<<"^2+4))="<<z<<endl;
z=(sqrt(fabs(x-1))-pow(fabs(y),3))/(1+x*x/2.+pow(y,3.)/3.);
cout<<" 4 - ое выражение (sqrt(fabs("<<x<<"-1))-pow(fabs("<<y<<"),3))/(1+"<<x<<"^2+pow("<<y<<"^3.)/3.)="<<z<<endl;
return 1;
}
Окно приложения будет имеет вид, показанный на рис. 25.
Рис. 25. Результат работы программы
Операции сравнения и логические операции будут рассмотрены в главе 6. В той же главе описывается операция(оператор) выбора.
Битовые операции
Битовые операции трактуют свои операнды как последовательности индивидуальных битов, а не числовые значения. Они работают только с целочисленными переменными или целыми константами в качестве операндов, поэтому с ними могут использоваться только типы данных short, int, long, signed char и char, а также их беззнаковые варианты. Битовые операции удобны для программирования аппаратных устройств, где состояние устройства часто представляется серией индивидуальных флагов (то есть, каждый бит может описывать состояние определенного аспекта данного устройства), или же их можно применять в ситуациях, когда желательно упаковать набор флагов "включено/выключено" в единую переменную.
Всего побитовых операций шесть:
& битовое И
| битовое ИЛИ
^ битовое исключающее ИЛИ
~ битовое НЕ
>> сдвиг вправо
<< сдвиг влево
Рассмотрим каждую из них подробнее.
Битовое И – бинарная операция.
Битовое И (&) – это бинарная операция, которая комбинирует соответствующие биты своих операндов определенным образом. Если в обоих операндах соответствующие биты равны 1, то и в результате будет 1, если же оба или один из битов операндов равен 0, то и в результате соответствующий бит будет равен 0.
Эффект применения конкретной битовой операции часто показывают с помощью таблицы истинности. В ней показаны все возможные комбинации битов операндов и соответствующий результирующий бит, полученный в результате применения оператора.
Пример работы &
char letterl = 'A', Ietter2 = 'Z', result = 0; result = letterl & Ietter2;
Для того чтобы понять, что произойдет, нужно взглянуть на битовые шаблоны. Буквы ‘А’ и 'Z' соответствуют шестнадцатеричным значениями 0x41 и 0х5А, соответственно (см. в приложении Б таблицу кодов ASCII). Работа операции битового И с этими двумя значениями показана на рис. 26.
Рис. 26. Работа операции битового И
После присваивания result будет иметь значение 0x40, что соответствует символу ‘@’.
Поскольку & дает 0, если любой из соответствующих битов операндов равен 0, его можно применять в тех случаях, когда в переменной определенные биты должны были установлены в 0, независимо от их исходного значения. Это достигается созданием так называемой "маски" и комбинацией ее с исходной переменной с помощью &. Создается маска, специфицируя значение 1 в тех битах, в которых старое значение нужно сохранить, и 0 – в тех битах, значение которых нужно сбросить в 0. Результатом применения И к маске и другому целому будет бит 0 в тех позициях, где стоит 0 в маске, и неизменное значение бита исходной переменной там, где в маске стоит 1. Предположим, что имеется переменная letter типа char, где в целях демонстрации надо сбросить в 0 старшие 4 бита. Это очень просто достигается установкой значения маски 0x0F и комбинацией ее со значением letter посредством &:
letter = letter & 0x0F;
или, более коротко:
letter &= 0x0F;
Если изначально значение letter равно 0x41, то в результате любого из этих двух операторов оно станет равным 0x01. Эта операция проиллюстрирована на рис. 27.
Рис. 27. Работа операции ???
Нулевые биты маски сбрасывают в ноль соответствующие биты letter, а биты маски, равные 1, оставляют соответствующие биты letter неизмененными.
Аналогично можено использовать маску 0xF0, чтобы сохранить 4 старших бита и сбросить в ноль 4 младших.
Таким образом, следующий оператор:
letter &= 0xF0; даст в результате изменение значения letter с 0x41 на 0x40.
Битовое ИЛИ – бинарный операция.
Битовое ИЛИ (|), иногда называемое включающим ИЛИ, комбинирует соответствующие биты операндов так, что результат равен 1, если хотя бы один из битов операндов в данной позиции равен 1, и 0, если оба бита равны 0.
Можно проверить это на примере установки индивидуальных флагов, упакованных в переменную типа int. Предположим, что имеется переменная по имени style типа short, которая содержит 16 индивидуальных 1-битных флагов. Предположим далее, что необходимо установить отдельные флаги в переменной style. Одним из способов сделать это является определение значений, которые можно скомбинировать операцией ИЛИ для установки определенных битов в 1. Чтобы установить крайний правый бит, можно определить переменную:
short vredraw = 0x01;
Чтобы установить второй справа бит, нужно определить вторую переменную:
short hredraw = 0x02;
Тогда для установки двух правых крайних битов переменной style в 1 можно воспользоваться следующим оператором:
style = hredraw | vredraw;
Эффект от этого оператора проиллюстрирован на рис. 28.
Рис. 28. Работа оператора ???
Поскольку операция ИЛИ дает в результате 1, когда любой из двух битов операндов равен 1, то объединение с ее помощью двух переменных дает в результате установку обоих бит.
Очень часто в программах предъявляется требование иметь возможность установить набор определенных флагов, не трогая остальных, которые могут устанавливаться где-то в другом месте. Можете легко сделать это с помощью такого оператора:
style |= hredraw > vredraw;
Этот оператор установит два крайних правых бита переменной style в 1, оставляя нетронутыми все остальные.
Битовое исключающее ИЛИ – бинарная операция.
Битовое исключающее ИЛИ (^), так называется потому, что работает подобно обычному ИЛИ, но когда оба бита операндов установлены в 1, возвращает 0. Применяя те же значения переменных, что уже были использованы с обычной операцией ИЛИ, можно посмотреть на результат такого оператора:
result = letterl^ Ietter2;
Эта операция может быть представлена следующим образом:
Letterl 0100 0001
Ietter2 0101 1010
Объединение их исключающим ИЛИ даст:
result 0001 1011
Переменная result получает значение 0х1В, или 27 в десятичной нотации.
Операция ^ имеет одно довольно неожиданное свойство. Предположим, что есть две переменных типа char — first со значением 'А' и last со значением 'Z' что в двоичном виде представляется как 0100 0001 и 0101 1010. Если записать такие операторы:
first ^= last; // в результате first равно 0001 1011
last ^= first; // в результате last равно 0100 0001
first ^= last; // в результате first равно 0101 1010
то first и last обменяются значениями без использования какой-либо памяти для промежуточного результата. Это работает с любыми целыми числами.
Битовое НЕ – унарный оператор.
Битовое НЕ (~) принимает единственный операнд, в котором инвертирует все биты: 1 становится 0, а 0 становится 1. Таким образом, если выполнить оператор
result = ~letterl;
то, если letterl равно 0100 0001, переменная result получит значение 1011 1110, то есть 0хВЕ, или 190 в десятичной нотации.