You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: lang_c/4/1__math_function/article.md
+71-18Lines changed: 71 additions & 18 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,9 +2,9 @@
2
2
3
3
Математические вычисления не ограничиваются лишь арифметическими действиями. Кроме них, можно ещё встретить корни, модули, логарифмы, тригонометрические функции и пр. Научимся же использовать подобные функции в своих программах.
4
4
5
-
Для использования математических функций нужно подключить заголовочный файл `math.h`. В этом файле объявлены различные математические функций и константы, но мы пока рассмотрим следующие:
5
+
Для использования математических функций нужно подключить заголовочный файл `math.h`.
6
6
7
-
Некоторые математические функции
7
+
Некоторые математические функции:
8
8
9
9
-`fabs(x)` -- модуль числа `x`
10
10
-`sqrt(x)` -- квадратный корень из числа `x`
@@ -17,14 +17,17 @@
17
17
18
18
Два важных момента.
19
19
20
-
- Все функции возвращают значение типа `double`.
21
-
- В качестве аргументов эти функции принимают вещественные числа типа `double`, но можно передать и числа других типов (например, `int`, `float`). При этом произойдёт =неявное преобразование типа=. Компилятор из целого числа, например `3`, сделает вещественное `3.0`.
20
+
- Все указанные функции возвращают значение типа `double`.
22
21
23
-
**Примеры:**
24
-
Даны длины катетов прямоугольного треугольника. Вычислить длину гипотенузы. Простая задачка на знание теоремы Пифагора.
22
+
- В качестве аргументов эти функции принимают вещественные числа типа `double`, но можно передать и числа других типов (например, `int`, `float`). При этом произойдёт =неявное преобразование (приведение) типа=. Компилятор из целого числа, например `3`, сделает число `3.0`, которое будет иметь тип `double`.
25
23
26
-
Листинг 1.
24
+
### Примеры использования математический функций:
25
+
26
+
**Задача 1:** Даны длины катетов прямоугольного треугольника. Вычислить длину гипотенузы.
27
27
28
+
Простая задачка на знание теоремы Пифагора.
29
+
30
+
Листинг 1.
28
31
```c
29
32
#include<stdio.h>
30
33
#include<math.h>// подключаем math.h
@@ -37,16 +40,18 @@ int main (void)
37
40
scanf("%d", &b);
38
41
39
42
c2 = a*a + b*b;
40
-
printf("c = %.2f\n", sqrt(c2));
41
-
43
+
printf("c = %.2f\n", sqrt(c2)); // так как sqrt возвращает double,
44
+
// то используем спецификатор %f
42
45
return 0;
43
46
}
44
47
```
45
48
46
-
Вычислить синус угла ввёденного с клавиатуры. Угол вводится в градусах.
49
+
Из интересного в этой программе лишь неявное преобразование типа, которое происходит, когда мы вызываем функцию `sqrt`. Допустим, мы запустили программу и ввели `3` и `4`. В переменную `c2` (типа `int`) будет записано значение `3*3 + 4*4 = 25`. Когда мы пишем вызов функции `sqrt(c2)`, то вместо `c2`, как мы уже знаем, подставляется значение, которое в ней хранится, т.е. `25`, получаем: `sqrt(25)`. Но как нам уже известно, функция `sqrt` ждёт от нас значения типа `double`, а мы ей передали значение типа `int`. Поэтому компилятор сначала выполнит неявное приведение типа: преобразует целое значение `3` в вещественное значение `3.0`.
47
50
48
-
Листинг 2.
49
51
52
+
**Задача 2:** Вычислить синус угла, введённого с клавиатуры. Угол вводится в градусах.
53
+
54
+
Листинг 2.
50
55
```c
51
56
#include <stdio.h>
52
57
#include <math.h> // подключаем math.h
@@ -66,24 +71,56 @@ int main (void)
66
71
}
67
72
```
68
73
69
-
В этой программе есть о чём поговорить.
70
-
71
-
Тригонометрические функции из файла `math.h` работают с радианной мерой угла (угол, заданный в радианах). Людям же привычнее работать с градусной мерой угла (углом, заданным в градусах). Поэтому в данной программе мы предварительно [перевели значение из градусов в радианы](https://stepik.org/lesson/%D0%90%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D0%BA%D0%B0-%D0%B2-%D0%A1%D0%B8-40857/step/9). Если этого не сделать, результат получится неправильным. Проверьте это самостоятельно.
74
+
Тригонометрические функции математической библиотеки языка Си работают с радианной мерой угла (угол, заданный в радианах). Людям же привычнее работать с градусной мерой угла (углом, заданным в градусах). Поэтому в данной программе мы предварительно [переводим значение из градусов в радианы](https://stepik.org/lesson/%D0%90%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D0%BA%D0%B0-%D0%B2-%D0%A1%D0%B8-40857/step/9).
75
+
Если этого не сделать, результат получится неправильным. Проверьте это самостоятельно.
72
76
73
77
## Неявное преобразование типов
74
78
При явном преобразовании типа мы в скобках перед значением указывали тип, к которому нужно привести данное значение. В неявном преобразовании этого делать не нужно. Компилятор автоматически подберёт необходимый тип.
75
79
80
+
%
76
81
Неявное преобразование типов осуществляется в следующих случаях:
77
-
78
-
1. перед передачей аргументов в функцию (как в нашем примере с корнем. Листинг 1.)
82
+
1. перед передачей аргументов в функцию
79
83
2. выполнение арифметических операций с разными типами аргументов
80
84
3. перед выполнением присваивания
81
85
86
+
Неявное преобразование типа при передаче аргумента в функцию мы обсудили выше (вызов функции `sqrt` в Листинге 1).
87
+
88
+
Пример неявного преобразования типа из пункта `2` мы встречали в прошлой заметке, когда разбирались с тем, как получить правильный результат деления `7` на `2`. Давайте посмотрим на Листинг 3 из прошлого шага:
89
+
90
+
Листинг 3.
91
+
```c
92
+
#include<stdio.h>
93
+
94
+
intmain(void)
95
+
{
96
+
int a = 7, b = 2;
97
+
float res;
98
+
99
+
res = (float) a / b;
100
+
printf("%d / %d = %f\n", a, b, res);
101
+
102
+
return 0;
103
+
}
104
+
```
105
+
106
+
Разберём поподробнее, как будет обрабатываться строка `res = (float) a / b;`.
107
+
108
+
Сначала подставим значения переменных `a` и `b`: `res = (float) 7 / 2;`
109
+
Далее произведём явное приведение типа для значения `7`: `res = 7.0 / 2;`
110
+
111
+
Т.к. процессоры умеют выполнять арифметические операции только с операндами одного и того же типа, то компилятору нужно произвести неявное приведение типа для значения `2`. Поэтому на этом этапе значение `2` преобразуется в значение `2.0`. Получаем: `res = 7.0 / 2.0;`
112
+
113
+
Получается, что в прошлом уроке мы изучили небольшой программистский "хак", связанный с преобразованием типов. Для правильного выполнения деления мы должны были бы сами явно привести оба числа к типу `float`, но мы схитрили и явно привели к типу `float` только одно число, а второе число преобразовал уже компилятор неявно. =)
114
+
115
+
82
116
### Правила неявного преобразования типов
83
117
84
-
* если выполняются арифметические операции с разными типами аргументов. Оба аргумента приводятся к большему типу. Старшинство типов: `int` < `float` < `double`
118
+
% **Важно!**
119
+
* если выполняются арифметические операции с разными типами аргументов, Оба аргумента приводятся к большему типу. Старшинство типов: `int` < `float` < `double`
120
+
* при вызове функций, переданные аргументы преобразуются к типам данных, которые ожидает функция.
121
+
* при присваивании. Значение справа от оператора присваивания приводится к типу переменной слева от оператора присваивания.
85
122
86
-
* при присваивании. Значение справа от оператора присваивания приводится к типу переменной слева от оператора присваивания. При этом, если больший тип присваивается меньшему, то может произойти =потеря точности=.
123
+
Снова обращаю ваше внимание на то, что если при неявном преобразовании типов может произойти =потеря точности=.
87
124
88
125
**Примеры:**
89
126
@@ -93,3 +130,19 @@ int main (void)
93
130
- `int = double` `double` будет преобразовано к `int` с потерей дробной части
94
131
- `float = int` `int` будет преобразовано к `float`
95
132
133
+
Учитывая разобранные правила, посмотрим на Листинг 4 из прошлого урока.
134
+
135
+
Листинг 4.
136
+
```c
137
+
int a = 7, b;
138
+
float g = 9.81, v;
139
+
140
+
b = (int) g; // приводим значение 9.81 к типу int, получим 9
141
+
v = (float) a; // приводим значение 7 к типу float, получим 7.0
142
+
```
143
+
144
+
В этом кусочке кода мы явно преобразовали тип значений справа от оператора присваивания. Но теперь, зная правила неявного преобразования типов, мы понимаем, что этого можно было бы и не делать, т.к. компилятор провёл бы эту процедуру самостоятельно. Т.е. можно было бы просто записать:
145
+
```c
146
+
b = g; \\ значение 9.81 будет неявно преобразовано к типу int, получим 9
147
+
v = a; \\ значение 7 будет неявно преобразовано к типу float, получим 7.0
"description": "Учимся использовать стандартные математические функции заголовочного файла math.h",
4
-
"keywords": "математические функции в си, заголовочный файл math.h, неявное преобразование типов",
3
+
"description": "Изучаем работу со стандартными математическими функциями заголовочного файла math.h, а также обсуждаем правила неявное преобразование типов.",
4
+
"keywords": "математические функции в си, заголовочный файл math.h, неявное преобразование типов, правила неявного преобразования типов, старшинство типов данных",
- Вычислите диапазон типа `int` в вашей системе (минимально и максимальное число, которое можно сохранить в переменную типа `int`). Используйте [решение задачи](https://stepik.org/edit-lesson/41090/step/8) и следующие факты:
10
+
1. Количество **байт**, выделенное под тип `int` в вашей системе можно узнать с помощью функции `sizeof()`. Если передать ей имя тип, то она вернёт целое число -- количество байт, выделенное под хранение этого типа данных. `printf("%d\n", sizeof(int));`
11
+
2.`1 байт = 8 бит`
12
+
3. Тип `int` по умолчанию является знаковым, а поэтому один бит в нём отводится на хранение знака числа.
13
+
4. Нуль тоже число и его тоже нужно хранить в памяти.
14
+
15
+
Попробуйте сохранить полученные значения в переменные типа `int` и вывести их на экран. О результатах пишите в комментариях к этому уроку.
16
+
9
17
### Исследовательские задачи для хакеров
10
18
11
19
1\. Разберитесь, почему следующая программа работает некорректно.
@@ -28,4 +36,7 @@ int main(void)
28
36
29
37
return 0;
30
38
}
31
-
```
39
+
```
40
+
41
+
2\. Найдите на своём компьютере заголовочный файл `math.h` и изучите его содержимое. Думаю, что вы удивитесь тому, что в этом файле будут только заголовки функций, без тела функций (т.е. без описания того, как они работают). Выясните самостоятельно, почему это так и где найти внутреннюю реализацию математических функций.
Copy file name to clipboardExpand all lines: lang_c/4/1__math_function/reference.md
+23-1Lines changed: 23 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,4 +2,26 @@
2
2
3
3
1\. Изначально `math.h` создавался для различных математических функций, работающих с числами с плавающей точкой (это способ приближённого представления вещественных чисел в памяти компьютера). Поэтому в заголовочном файле `math.h` вычисление модуля реализовано только для вещественных чисел. Например, `fabs()` для чисел типа `double` или `fabsf()` для чисел типа `float`.
4
4
5
-
Но существует также функция `abs()`, которая вычисляет модуль для целых чисел, но она объявлена в заголовочном файле `stdlib.h`.
5
+
Но существует также функция `abs()`, которая вычисляет модуль для целых чисел, но она объявлена в заголовочном файле `stdlib.h`.
6
+
7
+
2\. Может показаться, что потеря точности происходит только при преобразовании вещественных чисел в целые, когда отбрасывается дробная часть. Но это не так. Следующий программа демонстрирует, что проблемы могут происходить и при преобразовании целых чисел в вещественные (`int` в `float`).
8
+
9
+
```c
10
+
#include<stdio.h>
11
+
12
+
intmain(void)
13
+
{
14
+
int big_num = 123456789;
15
+
float float_num = big_num; // неявно преобразуем int в float ПРОБЛЕМА
16
+
int back_to_int = float_num; // неявно преобразуем float в int
17
+
18
+
printf("source: %d\n", big_num);
19
+
// printf("float: %f\n", float_num); // раскомментируйте, чтобы посмотреть на проблему
0 commit comments