Что такое float в с
Как и во многих языках программирования, в C# есть своя система типов данных, которая используется для создания переменных. Тип данных определяет внутреннее представление данных, множество значений, которые может принимать объект, а также допустимые действия, которые можно применять над объектом.
В языке C# есть следующие базовые типы данных:
-
bool : хранит значение true или false (логические литералы). Представлен системным типом System.Boolean
bool alive = true; bool isDead = false;
byte bit1 = 1; byte bit2 = 102;
sbyte bit1 = -101; sbyte bit2 = 102;
short n1 = 1; short n2 = 102;
ushort n1 = 1; ushort n2 = 102;
int a = 10; int b = 0b101; // бинарная форма b =5 int c = 0xFF; // шестнадцатеричная форма c = 255
uint a = 10; uint b = 0b101; uint c = 0xFF;
long a = -10; long b = 0b101; long c = 0xFF;
ulong a = 10; ulong b = 0b101; ulong c = 0xFF;
char a = 'A'; char b = '\x5A'; char c = '\u0420';
string hello = "Hello"; string word = "world";
object a = 22; object b = 3.14; object c = "hello code";
Например, определим несколько переменных разных типов и выведем их значения на консоль:
string name = "Tom"; int age = 33; bool isEmployed = false; double weight = 78.65; Console.WriteLine($"Имя: "); Console.WriteLine($"Возраст: "); Console.WriteLine($"Вес: "); Console.WriteLine($"Работает: ");
Для вывода данных на консоль здесь применяется интерполяция: перед строкой ставится знак $ и после этого мы можем вводить в строку в фигурных скобках значения переменных. Консольный вывод программы:
Имя: Tom Возраст: 33 Вес: 78,65 Работает: False
Использование суффиксов
При присвоении значений надо иметь в виду следующую тонкость: все вещественные литералы (дробные числа) рассматриваются как значения типа double . И чтобы указать, что дробное число представляет тип float или тип decimal , необходимо к литералу добавлять суффикс: F/f — для float и M/m — для decimal.
float a = 3.14F; float b = 30.6f; decimal c = 1005.8M; decimal d = 334.8m;
Подобным образом все целочисленные литералы рассматриваются как значения типа int . Чтобы явным образом указать, что целочисленный литерал представляет значение типа uint, надо использовать суффикс U/u , для типа long — суффикс L/l , а для типа ulong — суффикс UL/ul :
uint a = 10U; long b = 20L; ulong c = 30UL;
Использование системных типов
Выше при перечислении всех базовых типов данных для каждого упоминался системный тип. Потому что название встроенного типа по сути представляет собой сокращенное обозначение системного типа. Например, следующие переменные будут эквивалентны по типу:
int a = 4; System.Int32 b = 4;
Неявная типизация
Ранее мы явным образом указывали тип переменных, например, int x; . И компилятор при запуске уже знал, что x хранит целочисленное значение.
Однако мы можем использовать и модель неявной типизации:
var hello = "Hell to World"; var c = 20;
Для неявной типизации вместо названия типа данных используется ключевое слово var . Затем уже при компиляции компилятор сам выводит тип данных исходя из присвоенного значения. Так как по умолчанию все целочисленные значения рассматриваются как значения типа int , то поэтому в итоге переменная c будет иметь тип int . Аналогично переменной hello присваивается строка, поэтому эта переменная будет иметь тип string
Эти переменные подобны обычным, однако они имеют некоторые ограничения.
Во-первых, мы не можем сначала объявить неявно типизируемую переменную, а затем инициализировать:
// этот код работает int a; a = 20; // этот код не работает var c; c= 20;
Во-вторых, мы не можем указать в качестве значения неявно типизируемой переменной null :
// этот код не работает var c=null;
Так как значение null, то компилятор не сможет вывести тип данных.
Вещественные типы (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.0000000000000000
Print ( «2. c1 = » , DoubleToString (c1,-16));
// Результат: 2. c1 = 1.1212351499999999e-025
Print ( «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
*/
Типы данных
Типы данных имеют особенное значение в C#, поскольку это строго типизированный язык. Это означает, что все операции подвергаются строгому контролю со стороны компилятора на соответствие типов, причем недопустимые операции не компилируются. Следовательно, строгий контроль типов позволяет исключить ошибки и повысить надежность программ. Для обеспечения контроля типов все переменные, выражения и значения должны принадлежать к определенному типу. Такого понятия, как «бестиповая» переменная, в данном языке программирования вообще не существует. Более того, тип значения определяет те операции, которые разрешается выполнять над ним. Операция, разрешенная для одного типа данных, может оказаться недопустимой для другого.
В C# имеются две общие категории встроенных типов данных: типы значений и ссылочные типы. Они отличаются по содержимому переменной. Концептуально разница между ними состоит в том, что тип значения (value type) хранит данные непосредственно, в то время как ссылочный тип (reference type) хранит ссылку на значение.
Эти типы сохраняются в разных местах памяти: типы значений сохраняются в области, известной как стек, а ссылочные типы — в области, называемой управляемой кучей.
Давайте разберем типы значений.
Целочисленные типы
В C# определены девять целочисленных типов: char, byte, sbyte, short, ushort, int, uint, long и ulong. Но тип char применяется, главным образом, для представления символов и поэтому рассматривается отдельно. Остальные восемь целочисленных типов предназначены для числовых расчетов. Ниже представлены их диапазон представления чисел и разрядность в битах:
Тип | Тип CTS | Разрядность в битах | Диапазон |
---|---|---|---|
byte | System.Byte | 8 | 0:255 |
sbyte | System.SByte | 8 | -128:127 |
short | System.Int16 | 16 | -32768 : 32767 |
ushort | System.UInt16 | 16 | 0 : 65535 |
int | System.Int32 | 32 | -2147483648 : 2147483647 |
uint | System.UInt32 | 32 | 0 : 4294967295 |
long | System.Int64 | 64 | -9223372036854775808 : 9223372036854775807 |
ulong | System.UInt64 | 64 | 0 : 18446744073709551615 |
Как следует из приведенной выше таблицы, в C# определены оба варианта различных целочисленных типов: со знаком и без знака. Целочисленные типы со знаком отличаются от аналогичных типов без знака способом интерпретации старшего разряда целого числа. Так, если в программе указано целочисленное значение со знаком, то компилятор C# сгенерирует код, в котором старший разряд целого числа используется в качестве флага знака. Число считается положительным, если флаг знака равен 0, и отрицательным, если он равен 1.
Отрицательные числа практически всегда представляются методом дополнения до двух, в соответствии с которым все двоичные разряды отрицательного числа сначала инвертируются, а затем к этому числу добавляется 1.
Вероятно, самым распространенным в программировании целочисленным типом является тип int. Переменные типа int нередко используются для управления циклами, индексирования массивов и математических расчетов общего назначения. Когда же требуется целочисленное значение с большим диапазоном представления чисел, чем у типа int, то для этой цели имеется целый ряд других целочисленных типов.
Так, если значение нужно сохранить без знака, то для него можно выбрать тип uint, для больших значений со знаком — тип long, а для больших значений без знака — тип ulong. В качестве примера ниже приведена программа, вычисляющая расстояние от Земли до Солнца в сантиметрах. Для хранения столь большого значения в ней используется переменная типа long:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 < class Program < static void Main(string[] args) < long result; const long km = 149800000; // расстояние в км. result = km * 1000 * 100; Console.WriteLine(result); Console.ReadLine(); >> >
Всем целочисленным переменным значения могут присваиваться в десятичной или шестнадцатеричной системе обозначений. В последнем случае требуется префикс 0x:
long x = 0x12ab;
Если возникает какая-то неопределенность относительно того, имеет ли целое значение тип int, uint, long или ulong, то по умолчанию принимается int. Чтобы явно специфицировать, какой другой целочисленный тип должно иметь значение, к числу можно добавлять следующие символы:
uint ui = 1234U; long l = 1234L; ulong ul = 1234UL;
U и L можно также указывать в нижнем регистре, хотя строчную L легко зрительно спутать с цифрой 1 (единица).
Типы с плавающей точкой
Типы с плавающей точкой позволяют представлять числа с дробной частью. В C# имеются две разновидности типов данных с плавающей точкой: float и double. Они представляют числовые значения с одинарной и двойной точностью соответственно. Так, разрядность типа float составляет 32 бита, что приближенно соответствует диапазону представления чисел от 5E-45 до 3,4E+38. А разрядность типа double составляет 64 бита, что приближенно соответствует диапазону представления чисел от 5E-324 до 1,7Е+308.
Тип данных float предназначен для меньших значений с плавающей точкой, для которых требуется меньшая точность. Тип данных double больше, чем float, и предлагает более высокую степень точности (15 разрядов).
Если нецелочисленное значение жестко кодируется в исходном тексте (например, 12.3), то обычно компилятор предполагает, что подразумевается значение типа double. Если значение необходимо специфицировать как float, потребуется добавить к нему символ F (или f):
float f = 12.3F;
Десятичный тип данных
Для представления чисел с плавающей точкой высокой точности предусмотрен также десятичный тип decimal, который предназначен для применения в финансовых расчетах. Этот тип имеет разрядность 128 бит для представления числовых значений в пределах от 1Е-28 до 7,9Е+28. Вам, вероятно, известно, что для обычных арифметических вычислений с плавающей точкой характерны ошибки округления десятичных значений. Эти ошибки исключаются при использовании типа decimal, который позволяет представить числа с точностью до 28 (а иногда и 29) десятичных разрядов. Благодаря тому что этот тип данных способен представлять десятичные значения без ошибок округления, он особенно удобен для расчетов, связанных с финансами:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 < class Program < static void Main(string[] args) < // *** Расчет стоимости капиталовложения с *** // *** фиксированной нормой прибыли*** decimal money, percent; int i; const byte years = 15; money = 1000.0m; percent = 0.045m; for (i = 1; i Console.WriteLine("Общий доход за лет: $$",years,money); Console.ReadLine(); > > >
Результатом работы данной программы будет:
Символы
В C# символы представлены не 8-разрядным кодом, как во многих других языках программирования, например С++, а 16-разрядным кодом, который называется юникодом (Unicode). В юникоде набор символов представлен настолько широко, что он охватывает символы практически из всех естественных языков на свете. Если для многих естественных языков, в том числе английского, французского и немецкого, характерны относительно небольшие алфавиты, то в ряде других языков, например китайском, употребляются довольно обширные наборы символов, которые нельзя представить 8-разрядным кодом. Для преодоления этого ограничения в C# определен тип char, представляющий 16-разрядные значения без знака в пределах от 0 до 65 535. При этом стандартный набор символов в 8-разрядном коде ASCII является подмножеством юникода в пределах от 0 до 127. Следовательно, символы в коде ASCII по-прежнему остаются действительными в C#.
Для того чтобы присвоить значение символьной переменной, достаточно заключить это значение (т.е. символ) в одинарные кавычки:
char ch; ch = 'Z';
Несмотря на то что тип char определен в C# как целочисленный, его не следует путать со всеми остальными целочисленными типами. Дело в том, что в C# отсутствует автоматическое преобразование символьных значений в целочисленные и обратно. Например, следующий фрагмент кода содержит ошибку:
char ch; ch = 8; // ошибка, не выйдет
Наравне с представлением char как символьных литералов, их можно представлять как 4-разрядные шестнадцатеричные значения Unicode (например, ‘\u0041’), целочисленные значения с приведением (например, (char) 65) или же шестнадцатеричные значения (например, ‘\x0041’). Кроме того, они могут быть представлены в виде управляющих последовательностей.
Логический тип данных
Тип bool представляет два логических значения: «истина» и «ложь». Эти логические значения обозначаются в C# зарезервированными словами true и false соответственно. Следовательно, переменная или выражение типа bool будет принимать одно из этих логических значений. Кроме того, в C# не определено взаимное преобразование логических и целых значений. Например, 1 не преобразуется в значение true, а 0 — в значение false.
C++: Типы данных с плавающей точкой
Целочисленные типы отлично подходят для подсчета целых чисел, но иногда нам нужно хранить очень большие числа или числа с дробной частью. Переменная типа с плавающей точкой – это переменная, которая может содержать действительное число, например 4320,0, -3,33 или 0,01226. Название плавающая точка указывает на то, что десятичная точка может плавать, то есть она может поддерживать переменное количество цифр до и после себя.
Существует три разных типа данных с плавающей точкой: float , double и long double . Как и в случае с целыми числами, C++ не определяет фактические размеры этих типов, но гарантирует минимальные размеры:
- float – должен иметь как минимум 32-бита и гарантировать минимум 6 значащих цифр
- double – 48-бит и быть не меньше float. Гарантирует 15 значащих цифр
- long double – как минимум 80 битов и 18-значащих цифр
Типы данных с плавающей точкой всегда идут со знаком, то есть могут содержать положительные и отрицательные значения.
Ниже представлены примеры определения переменных с плавающей точкой:
float f_num; double d_num; long double ld_num;
Вы могли заметить, что в имени переменной используется префикс — это делать не обязательно, но это помогает понять переменная кого типа сейчас используется.
При инициализации переменных такого типа всегда включайте хотя бы один знак после десятичной точки, даже если это ноль. Так компилятор будет понимать, что переменная принадлежит к вещественному типу.
double d_num < 5.0 >; float f_num < 5.0f >;
По умолчанию литералы с плавающей точкой имеют тип double . Суффикс f используется для обозначения литерала типа float .
Вы могли заметить, что в коде выше инициализация производится не через оператор присваивания = , а с помощью нотации < 5.0 >. Это списковая инициализация, она пришла в язык с 11 стандарта и она имеет одно преимущество:
int num; num = 3.14; std::cout 3
В коде выше мы пытаемся сохранить в целочисленную переменную вещественное число. Такая программа скомпилируется и будет работать, но у числа мы потеряем всю вещественную часть.
Если применить списковую инициализацию, получим ошибку компиляции:
int num < 3.14 >; // ошибка int count; count = < 3.14 >; // ошибка
Рассмотрим такую программу:
int main() < float f_num = < 10.0 / 3.0 >; double d_num = < 10.0 / 3.0 >; std::cout
3.33333 3.33333
Обратите внимание, что каждое из напечатанных значений имеет только 6 значащих цифр. Это стандартное поведение объекта cout , но мы можем переопределить это поведение и повысить точность:
#include #include // для увеличения точности int main() < std::cout ; double d_num = < 10.0 / 3.0 >; std::cout
3.333333253860474 3.333333333333333
Видно, что число float определено не точно и содержит ошибки, поскольку тип float гарантирует нам только шесть значащих цифр.
Существует две особые категории чисел с плавающей точкой. Первая – Inf , которая представляет бесконечность. Inf может быть положительной или отрицательной. Вторая – NaN , что означает Not a Number — не число. Существует несколько различных типов NaN . NaN и Inf доступны только в том случае, если компилятор для чисел с плавающей точкой использует определенный формат. Если используется другой формат, следующий код приводит к неопределенному поведению.
#include int main() < double zero < 0.0 >; double pos_inf < 5.0 / zero >; // положительная бесконечность std::cout ; // отрицательная бесконечность std::cout ; // не число (математически неверно) std::cout
inf -inf nan
Задание
Напишите программу, которая рассчитывает сколько километров проедет автомобиль на полном баке. Пусть объем бака будет 43 литра, а расход 8.8 л на 10 км. Результат выведите на экран.
Упражнение не проходит проверку — что делать?
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
- Обязательно приложите вывод тестов, без него практически невозможно понять что не так, даже если вы покажете свой код. Программисты плохо исполняют код в голове, но по полученной ошибке почти всегда понятно, куда смотреть.
В моей среде код работает, а здесь нет
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Мой код отличается от решения учителя
Это нормально , в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Прочитал урок — ничего не понятно
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.