Действительные типы данных в языке C
Для представления действительных (вещественных) чисел в языке C и C++ существует три типа данных: float, double, long double. Основным из этих типов является double, в большинстве случаев следует использовать именно его.
Операции с действительными типами данных
Для действительных типов данных допустимы операции сложения, вычитания, умножения и деления. При этом операция деления возвращает значение действительного типа, если хотя бы один операнд (то есть то, что записано слева или справа) является действительным числом (содержит точку в записи) или является переменной действительного типа. Если же оба операнда будут целочисленными константами, переменными или выражениями целочисленного типа, то деление будет производиться нацело, с отбрасыванием дробной части, и результатом будет значение целочисленного типа.
Например, при делении 10 / 4 в результате получится 2, а при делении 10.0 на 4 в результате получится 2.5.
Ввод-вывод переменных действительного типа
Для ввода-вывода переменной типы double используется (в современных вариантах стандарта языка C) форматная строка «%lf»:
Д. П. Кириенко. Программирование на языке Python (школа 179 г. Москвы)
В этом листочке речь пойдет о действительных числах, имеющих тип float .
Обратите внимание, что если вы хотите считать с клавиатуры действительное число, то результат, возращаемый функцией input() необходимо преобразовывать к типу float :
x = float(input())
Действительные (вещественные) числа представляются в виде чисел с десятичной точкой (а не запятой, как принято при записи десятичных дробей в русский текстах). Для записи очень больших или очень маленьких по модулю чисел используется так называемая запись “с плавающей точкой” (также называемая “научная” запись). В этом случае число представляется в виде некоторой десятичной дроби, называемой мантиссой, умноженной на целочисленную степень десяти (порядок). Например, расстояние от Земли до Солнца равно 1.496·10 11 , а масса молекулы воды 2.99·10 -23 .
Числа с плавающей точкой в программах на языке Питон, а также при вводе и выводе записавыются в виде мантиссы, затем пишется буква e , затем пишется порядок. Пробелы внутри этой записи не ставятся. Например, указанные выше константы можно записать в виде 1.496e11 и 2.99e-23 . Перед самим числом также может стоять знак минус.
Напомним, что результатом операции деления / всегда является действительное число, в то время как результатом операции // является целое число.
Преобразование действительных чисел к целому производится с округлением в сторону нуля, то есть int(1.7) == 1 , int(-1.7) == -1 .
Действительные числа
Ранее рассматривался только один тип целочисленных переменных — int . На самом деле существует несколько основных целочисленных типов, тип int — лишь один (но наиболее часто используемый) из них.
Таблица основных целочисленных типов.
Название | Размер | Знаковый | Синонимы |
---|---|---|---|
short | 2 байта | Знаковый | short int , signed short , signed short int |
unsigned short | 2 байта | Беззнаковый | unsigned short int |
int | 4 байта | Знаковый | signed int |
unsigned | 4 байта | Беззнаковый | unsigned int |
long | 4 байта | Знаковый | long int , signed long , signed long int |
unsigned long | 4 байта | Беззнаковый | unsigned long int |
long long | 8 байт | Знаковый | long long int , signed long long , signed long long int |
unsigned long long | 8 байт | Беззнаковый | unsigned long long int |
То есть типы бывают “короткими” ( short ), обычными, длинными ( long ) и очень длинными ( long long ). Последний тип является расширением компилятора GNU C++ и не является стандартным типом для языка C++, поэтому он может отсутствовать в других реализациях языка или называться по-другому (например, в компиляторе Microsoft Visual C++ аналогичный тип называется int64 ). Чем “длиннее” тип, тем большее число различных значений он может принимать, тем больше памяти он занимает. Также типы бывают знаковыми ( signed ), которые могут принимать как положительные, так и отрицательные значения и беззнаковые ( unsigned ), которые принимают только неотрицательные значения.
Таблица значений, которые могут принимать различные типы:
Название | Размер | Минимальное значение | Максимальное значение |
---|---|---|---|
short | 16 бит | -2 15 =- 32768 | 2 15 -1 = 32767 |
unsigned short | 16 бит | 0 | 2 16 -1 = 65535 |
int , long | 32 бита | -2 31 = -2147483648 | 2 31 -1 = 2147483647 |
unsigned , unsigned long | 32 бита | 0 | 2 32 -1 = 4294967295 |
long long | 64 бита | -2 63 = -9223372036854775808 | 2 63 -1 = 9223372036854775807 |
unsigned long long | 64 бита | 0 | 2 64 -1 = 18446744073709551615 |
На самом деле в стандарте языка C++ не оговорены конкретные значения размеров типов. Оговорено только то, что одинаковые знаковые и беззнаковые типы имеют одинаковые размеры, и размер меньшего типа всегда не превосходит размера большего типа. Вот какие размеры могут быть у этих типов в зависимости от разрядности процессора компьютера:
Тип | 16-битный процессор | 32-битный процессор | 64-битный процессор |
---|---|---|---|
short | 2 байта | 2 байта | 2 байта |
int | 2 байта | 4 байта | 4 байта |
long | 4 байта | 4 байта | 8 байт |
long long | — | 8 байт | 8 байт |
Действительные типы
Действительные (вещественные) числа представляются в виде чисел с десятичной точкой (а не запятой, как принято при записи десятичный дробей в русский текстах). Для записи очень больших или очень маленьких по модулю чисел используется так называемая запись “с плавающей точкой” (также называемая “научная” запись). В этом случае число представляется в виде некоторой десятичной дроби, называемой мантиссой, умноженной на целочисленную степень десяти (порядок). Например, расстояние от Земли до Солнца равно 1.496·10 11 , а масса молекулы воды 2.99·10 -23 .
Числа с плавающей точкой в программах на языке C++, а также при вводы и выводе записавыются в виде мантиссы, затем пишется буква e , затем пишется порядок. Пробелы внутри этой записи не ставятся. Например, указанные выше константы можно записать в виде 1.496e11 и 2.99e-23 . Перед самим числом также может стоять знак минус.
Для представления в памяти ЭВМ действительных чисел существует три типа:
Тип | Точность | Размер | Количество знаков мантиссы | Минимальное положительное значение | Максимальное значение |
---|---|---|---|---|---|
float | Одинарная | 4 байта | 7 | 1.4e-45 | 3.4e38 |
double | Двойная | 8 байт | 15 | 5.0e-324 | 1.7e308 |
long double | Расширенная | 10 байт | 19 | 1.9e-4951 | 1.1e4932 |
Операция деления
Для действительных чисел определены операции сложения, вычитания, умножения и деления.
При этом операция деления выполняется по-разному для переменных и констант целочисленного типа и для переменных и констант действительных типов. В первом случае деление производится нацело с отбрасыванием дробной части, во втором случае — деление производится точно и результатом является действительное число. Более точно, если делимое и делитель одновременно являются целочисленными константами или переменными целочисленных типов, то деление будет целочисленным, а если хотя бы одно из них действительное, то деление будет действительным. Например:
coutвыведет 3 в первой строке и 3.33333 в остальных строках.
Результат выполнения деления не зависит от того, какой переменной будет присвоен результат. Если написать double a = 10 / 3; , то переменная a будет равна 3, так как деление 10/3 будет целочисленным, независимо от того, чему будет присвоен результат.
Приведение типов
Иногда возникает необходимость привести выражение одного типа к такому же выражению другого типа. Например, если есть две переменные a и b типа int и требуется вычислить их частное (не целочисленное) и записать в переменную d типа double . Следующий код:
double d; d = a / bбудет неверным, т.к. деление a / b будет целочисленным. Правильный код такой:
double d; d = (double)a / b;В этом примере используется операция приведения типа: (double)a . Эта операция возвращает значение типа double , но равное значению переменной a . В результате деление будет выполняться, как деление вещественных чисел, поскольку будет выполняться деление действительного значения на целочисленное.
Операция приведения типа не меняет тип самой переменной и ее значение, а только возвращает значение другого типа.
Упражнение
Определите, чему будут равны следующие переменные
int a = 13 / 5; int b = 13 % 5; int c = 13.0 / 5; double d = 13 / 5; double e = 13 % 5; double f = 13.0 / 5; double g = 13 / 5 + 2 / 5; double h = 13.0 / 5 + 2.0 / 5; int i = 13.0 / 5 + 2.0 / 5;Список функций
В стандартную математическую библиотеку языка Си (а, значит, и C++) входит множество специальных математических функций, которые нужно знать и уметь использовать. Для того, чтобы использовать эти функции в своей программе, необходимо подключить заголовочный файл, содержащий описания этих функций, что делается строчкой в начале программы:
#include
Функция от одного аргумента вызывается, например, так: sin(x) . Вместо числа x может быть любое число, переменная или выражение. Функция возращает значение, которое можно вывести на экран, присвоить другой переменной или использовать в выражении:
y = sin(x); coutТакже в файле cmath есть набор полезных числовых констант, например, константа M_PI хранит значение числа \(\pi\).
В компиляторе Visual C++ для использования этих констант необходимо объявить директиву препроцессора _USE_MATH_DEFINES перед подключения заголовочного файла cmath .
#define _USE_MATH_DEFINES #includeТочность вывода
По умолчанию действительные числа выводятся с точностью в 6 знаков, но в переменной типа double числа храняться с точностью в 15-16 знаков. Для того, чтобы вывести число с большей точностью, например, с точностью в 15 знаков, нужно выполнить команду:
cout.precision(15);Вещественные типы (double, float)
Вещественные типы (или типы с плавающей точкой) представляют значения, имеющие дробную часть. В языке MQL4 есть два типа для чисел с плавающей точкой. Способ представления вещественных чисел в машинной памяти определен стандартом IEEE 754 и не зависит от платформ, операционных систем и языков программирования.
Размер в байтах
Минимальное положительное значение
Имя double означает, что точность этих чисел вдвое превышает точность чисел типа float . В большинстве случаев тип double является наиболее удобным. Ограниченной точности чисел float во многих случаях попросту недостаточно. Причина, по которой тип float все еще используется, - экономия памяти при хранении (это важно для больших массивов вещественных чисел).
Константы с плавающей точкой состоят из целой части, точки (.) и дробной части. Целая и дробная части представляют собой последовательности десятичных цифр.
double a= 12.111 ;
double b=- 956.1007 ;
float c = 0.0001 ;
float d = 16 ;Существует научный способ записи вещественных констант, зачастую этот способ записи более компактный, чем традиционный.
double c1=1.12123515e-25;
double c2=0.000000000000000000000000112123515; // 24 нуля после десятичной точкиPrint ( "1. c1 = " , DoubleToString (c1,16));
// Результат: 1. c1 = 0.0000000000000000Print ( "2. c1 = " , DoubleToString (c1,-16));
// Результат: 2. c1 = 1.1212351499999999e-025Print ( "3. c2 = " , DoubleToString (c2,-16));
// Результат: 3. c2 = 1.1212351499999999e-025Необходимо помнить, что вещественные числа хранятся в памяти компьютера с некоторой ограниченной точностью в двоичной системе счисления, в то время как общепринятой в использовании является десятичная система счисления. Поэтому многие числа, которые точно записываются в десятичной системе, в двоичной системе можно записать только в виде бесконечной дроби.
Например, числа 0.3 и 0.7 представлены в компьютере бесконечными дробями, в то время как число 0.25 хранится точно, так как представляет из себя степень двойки.
В связи с этим, категорически не рекомендуется сравнивать между собой два вещественных числа на равенство, так как такое сравнение не является корректным.
void OnStart ()
<
//---
double three=3.0;
double x,y,z;
x=1/three;
y=4/three;
z=5/three;
if (x+y==z) Print ( "1/3 + 4/3 == 5/3" );
else Print ( "1/3 + 4/3 != 5/3" );
// Результат: 1/3 + 4/3 != 5/3
>Если все же необходимо сравнить на равенство два вещественных числа, то можно сделать это двумя разными способами. Первый способ заключается в сравнении разницы между двумя числами с какой-то малой величиной, задающей точность сравнения.
bool EqualDoubles( double d1, double d2, double epsilon)
<
if (epsilon <0) epsilon=-epsilon;
//---
if (d1-d2>epsilon) return false;
if (d1-d2 <-epsilon) return false;
//---
return true ;
>
void OnStart ()
<
double d_val=0.7;
float f_val=0.7;
if (EqualDoubles(d_val,f_val,0.000000000000001)) Print (d_val, "equals" ,f_val);
else Print ( "Different: d_val = " , DoubleToString (d_val,16),
" f_val = " , DoubleToString (f_val,16));
// Результат: Different: d_val= 0.7000000000000000 f_val= 0.6999999880790710
>Необходимо отметить, что значение параметра epsilon в приведенном примере не может быть меньше предопределенной константы DBL_EPSILON. Значение этой константы 2.2204460492503131e-016. Для типа float соответствующая константа FLT_EPSILON = 1.192092896e-07. Смысл этих значений таков, что это наименьшее значение, удовлетворяющее условию 1.0+DBL_EPSILON != 1.0 (для чисел типа float 1.0+FLT_EPSILON != 1.0).
Второй способ предполагает сравнивать нормализованную разность двух вещественных чисел с нулевым значением. Сравнивать разность нормализованных чисел с нулём бесполезно, так как в результате любой математической операции с нормализованными числами результат получается ненормализованным.
bool CompareDoubles( double number1, double number2)
<
if ( NormalizeDouble (number1-number2,8)==0) return ( true );
else return ( false );
>
void OnStart ()
<
double d_val=0.3;
float f_val=0.3;
if (CompareDoubles(d_val,f_val)) Print (d_val, "equals" ,f_val);
else Print ( "Different: d_val = " , DoubleToString (d_val,16),
" f_val = " , DoubleToString (f_val,16));
// Результат: Different: d_val= 0.3000000000000000 f_val= 0.3000000119209290
>В результате некоторых операций математического сопроцессора может получиться недействительное вещественное число, которое нельзя использовать в математических операциях и операциях сравнения, так как результат выполнения операций над недействительными вещественными числами неопределен. Например, при попытке вычислить арксинус от 2, результатом будет минус бесконечность.
double abnormal = MathArcsin (2.0);
Print ( "MathArcsin(2.0) =" ,abnormal);
// Результат: MathArcsin(2.0) = -1.#INDКроме минус бесконечности существуют плюс бесконечность и NaN (не число). Чтобы определить, что данное число недействительно, можно использовать функцию MathIsValidNumber(). По стандарту IEEE они имеют специальное машинное представление. Например, плюс бесконечность для типа double имеет битовое представление 0x7FF0 0000 0000 0000.
struct str1
<
double d;
>;
struct str2
<
long l;
>;//--- начнем
str1 s1;
str2 s2;
//---
s1.d= MathArcsin (2.0); // получим недействительное число -1.#IND
s2=s1;
printf ( "1. %f %I64X" ,s1.d,s2.l);
//---
s2.l=0xFFFF000000000000; // недействительное число -1.#QNAN
s1=s2;
printf ( "2. %f %I64X" ,s1.d,s2.l);
//---
s2.l=0x7FF7000000000000; // наиобльшее нечисло SNaN
s1=s2;
printf ( "3. %f %I64X" ,s1.d,s2.l);
//---
s2.l=0x7FF8000000000000; // наименьшее нечисло QNaN
s1=s2;
printf ( "4. %f %I64X" ,s1.d,s2.l);
//---
s2.l=0x7FFF000000000000; // наибольшее нечисло QNaN
s1=s2;
printf ( "5. %f %I64X" ,s1.d,s2.l);
//---
s2.l=0x7FF0000000000000; // плюс бесконечность 1.#INF и наименьшее нечисло SNaN
s1=s2;
printf ( "6. %f %I64X" ,s1.d,s2.l);
//---
s2.l=0xFFF0000000000000; // минус бесконечность -1.#INF
s1=s2;
printf ( "7. %f %I64X" ,s1.d,s2.l);
//---
s2.l=0x8000000000000000; // отрицательный ноль -0.0
s1=s2;
printf ( "8. %f %I64X" ,s1.d,s2.l);
//---
s2.l=0x3FE0000000000000; // 0.5
s1=s2;
printf ( "9. %f %I64X" ,s1.d,s2.l);
//---
s2.l=0x3FF0000000000000; // 1.0
s1=s2;
printf ( "10. %f %I64X" ,s1.d,s2.l);
//---
s2.l=0x7FEFFFFFFFFFFFFF; // наибольшее нормализованное число (MAX_DBL)
s1=s2;
printf ( "11. %.16e %I64X" ,s1.d,s2.l);
//---
s2.l=0x0010000000000000; // наименьшее положительное нормализованное (MIN_DBL)
s1=s2;
printf ( "12. %.16e %.16I64X" ,s1.d,s2.l);
//---
s1.d=0.7; // покажем, что число 0.7 – бесконечная дробь
s2=s1;
printf ( "13. %.16e %.16I64X" ,s1.d,s2.l);
/*
1. -1.#IND00 FFF8000000000000
2. -1.#QNAN0 FFFF000000000000
3. 1.#SNAN0 7FF7000000000000
4. 1.#QNAN0 7FF8000000000000
5. 1.#QNAN0 7FFF000000000000
6. 1.#INF00 7FF0000000000000
7. -1.#INF00 FFF0000000000000
8. -0.000000 8000000000000000
9. 0.500000 3FE0000000000000
10. 1.000000 3FF0000000000000
11. 1.7976931348623157e+308 7FEFFFFFFFFFFFFF
12. 2.2250738585072014e-308 0010000000000000
13. 6.9999999999999996e-001 3FE6666666666666
*/