Этот урок посвящен числовым типам и типу Boolean. Рассмотрены вопросы приведения типов, арифметические и логические операции, возможности класса Math и преобразование чисел в строку и обратно.
Исходный код примеров из этой статьи можете скачать из нашего github-репозитория.
Числовые типы
Приведение типов
При работе с числовыми типами данных довольно часто приходится сталкиваться с “расширением” и с “сужением” типа. Для начала рассмотрим несколько примеров:
byte b1 = 100; short s1 = b1; // расширение типа Console.WriteLine($"byte value: {b1}, short value: {s1}");
При компиляции этого кода никаких ошибок не будет несмотря на то, что мы присваиваем переменную одного типа (byte) переменной другого типа (short) для целых чисел это делать можно без последствий, так как в данном случае происходит расширение типа. Переменная типа byte может принимать значения из диапазона от -128 до 127, а short из диапазона от -32 768 до 32 767, что значительно превышает ограничения для byte. Таким образом, при присваивании значения переменной типа byte, переменной типа short не происходит потери данных. В обратную сторону это сделать не получится, если вы напишите код, приведенный ниже, то компиляция будет прервана с ошибкой:
short s2 = 100; byte b2 = s2; // приведет к ошибке компиляции
Для того, чтобы такая операция могла быть выполнена, необходимо использовать явное приведение, тем самым, мы как бы говорим компилятору, что в курсе того, что делаем. Для явного приведения необходимо тип, к которому приводится значение переменной, указать перед ней в круглых скобках. Перепишем наш второй пример с использованием явного приведения:
short s2 = 100; byte b2 = (byte)s2; Console.WriteLine($"byte value: {b2}, short value: {s2}");
Но имейте ввиду, что такая операция, по своей сути, не является безопасной, так как в этом случае возможно переполнение, что приведет к получению результата, который скорее всего вы не ожидаете:
short s3 = 150; short s4 = 150; byte b3 = (byte)(s3 + s4); Console.WriteLine($"Result: {b3}");
В результате на консоль будет выведено следующее:
Result: 44
Это произошло вследствие того, что сумма s3 и s4 равна 300, а максимальное значение, которое может храниться в byte – это 127.
Проверка переполнения
Если переполнение является критичным моментом для некоторого участка кода вашего приложения, то можете использовать проверку переполнения с помощью ключевого слова checked. Суть его работы заключается в том, что если в рамках контекста, обозначенного через checked происходит переполнение, то будет выброшено исключение OverflowException. Пример использования для одного оператора приведен ниже:
try { short s5 = 150; short s6 = 150; byte b4 = checked((byte)(s5 + s6)); } catch(OverflowException) { Console.WriteLine("Overflow is detected!"); }
Если необходимо провести проверку для группы операторов, то используйте checked следующим образом:
try { checked { short s5 = 150; short s6 = 150; byte b4 = checked((byte)(s5 + s6)); byte b5 = checked((byte)(s5 * 100)); } } catch(OverflowException) { Console.WriteLine("Overflow is detected!"); }
В результате выполнения любого из примеров, на консоль будет выведено сообщение:
Overflow is detected!
Для того, чтобы выполнять такого типа проверку для всех вычислений в вашем приложении необходимо активировать эту опцию на этапе компиляции. Если вы работаете в Visual Studio, то зайдите в свойства проекта, перейти на вкладку Build и нажмите на кнопку “Advanced…”. В результате откроется окно с настройками:
В нем можно поставить (или убрать) галочку в поле “Check for arithmetic overflow/underflow”. Если установить галочку, то все вычисления будут проверяться на переполнение. В таком случае можно отдельно создавать блоки кода, в которых данная проверка производиться не будет, для этого используйте оператор unchecked:
unchecked { short s5 = 150; short s6 = 150; byte b4 = checked((byte)(s5 + s6)); }
Класс System.Convert
Для приведения типов можно воспользоваться классом System.Convert, который содержит методы для приведения одного типа к другому. Подробная документация по его возможностям приведена в официальной документации.
Приведем несколько примеров его использования. Вариант, когда приведение типа не приводит к переполнению:
short s7 = 100; byte b6 = System.Convert.ToByte(s7); Console.WriteLine($"byte value: {b6}, short value: {s7}");
Вариант, когда приведение типа приводит к переполнению, в этом случае будет выброшено исключение:
short s8 = 500; byte b7 = System.Convert.ToByte(s8); Console.WriteLine($"byte value: {b7}, short value: {s8}");
Арифметические операции
В таблице ниже перечислены арифметические операции, которые можно выполнять над числовыми типами данных в C#.
Операция | Вид | Пример |
Сложение | a + b |
Console.WriteLine($”3 + 4={(3 + 4)}”) |
Вычитание | a – b |
Console.WriteLine($”7 – 5={(7 – 5)}”); |
Умножение | a * b |
Console.WriteLine($”2 * 9={(2 * 9)}”); |
Деление | a / b |
Console.WriteLine($”8 / 4={(8 / 4)}”); |
Остаток от деления | a % b |
Console.WriteLine($”8 % 4={(8 % 4)}”); |
Постфиксный инкремент | a++ | int n1 = 5; Console.WriteLine($”n1++: {n1++}”); Console.WriteLine($”n1 = {n1}”); |
Префиксный инкремент | ++a | int n2 = 5; Console.WriteLine($”++n2: {++n2}”); Console.WriteLine($”n2 = {n2}”); |
Постфиксный декремент | a– | int n3 = 9; Console.WriteLine($”n3–: {n3–}”); Console.WriteLine($”n3 = {n3}”); |
Префиксный инкремент | –a | int n4 = 9; Console.WriteLine($”–n4: {–n4}”); Console.WriteLine($”n4 = {n4}”); |
Составное присваивание | a += b a -= b a *= b a /= b a % b |
int d1 = 10; int d2 = 3; d1 += d2; Console.WriteLine($”d1 += d2: {d1}”); d1 -= d2; Console.WriteLine($”d1 -= d2: {d1}”); d1 *= d2; Console.WriteLine($”d1 *= d2: {d1}”); d1 /= d2; Console.WriteLine($”d1 /= d2: {d1}”); d1 %= d2; Console.WriteLine($”d1 %= d2: {d1}”); |
Из перечисленных выше арифметических операций, обратите внимание на операции инкремента и декремента. При использовании постфиксного варианта, вначале происходит возврат значения переменной, а потом выполнение операции, для префиксного наоборот.
Класс Math
Класс Math предоставляет реализации распространенных математических функций. Полный список представлен в официальной документации. Ниже будут рассмотрены некоторые из функций класса Math.
Функции округления
Math.Ceiling
Возвращает наименьшее число, которое больше либо равно заданному:
Console.WriteLine(Math.Ceiling(4.5)); // вернет 5
Math.Floor
Возвращает наибольшее число, которое меньше либо равно заданному:
Console.WriteLine(Math.Floor(4.5)); // вернет 4
Math.Round
Округляет число до ближайшего целого значения. Значение посередине округляется до ближайшего четного.
Console.WriteLine(Math.Round(4.7)); // вернет 5 Console.WriteLine(Math.Round(4.5)); // вернет 4 Console.WriteLine(Math.Round(5.5)); // вернет 6
Тригонометрические функции
Math.Cos
Косинус угла:
Console.WriteLine(Math.Cos(30));
Math.Sin
Синус угла:
Console.WriteLine(Math.Sin(30));
Math.Tan
Тангенс угла:
Console.WriteLine(Math.Tan(30));
Разное
Math.Exp
Возвращает число e в заданной степени:
Console.WriteLine(Math.Exp(Math.E)); // ответ: 1
Math.Log
Если функция вызывается с одним аргументом, то возвращается натуральный логарифм числа:
Console.WriteLine(Math.Log(Math.E)); // ответ: 1
Если с двумя аргументами, то возвращается логарифм числа по заданному основанию:
Console.WriteLine(Math.Log(9, 3)); // ответ: 2
Math.Log10
Возвращает десятичный логарифм числа:
Console.WriteLine(Math.Log10(100)); // ответ: 2
Math.Log2
Возвращает логарифм по основанию 2 от числа:
Console.WriteLine(Math.Log2(128)); // ответ: 7
Math.Pow
Возвращает число, возведенное в заданную степень:
Console.WriteLine(Math.Pow(5, 2)); // ответ: 25
Math.Abs
Возвращает абсолютное значение числа:
Console.WriteLine(Math.Abs(-10)); // ответ: 10
Math.Min
Возвращает наименьшее из переданных чисел:
Console.WriteLine(Math.Min(10, 7)); // ответ: 7
Math.Max
Возвращает наибольшее из переданных чисел:
Console.WriteLine(Math.Max(5, 2)); // ответ: 5
Math.Sign
Возвращает знак числа. Если число положительное, то будет возвращено значение 1, если отрицательное, то -1.
Console.WriteLine(Math.Sign(-2)); // ответ: -1
Преобразование числа в строку
Так как все классы числовых типов в C# являются наследниками от System.Object, то это означает, что у них у всех есть метод ToString(), который переводит численное значение в строковое представление:
Console.WriteLine(123.ToString()); int n5 = 701; Console.WriteLine(n5.ToString()); Console.WriteLine(5.234.ToString());
Для форматирования строкового представления числа с плавающей точкой используйте Format с соответствующим набором маркеров, пример:
Console.WriteLine(String.Format("{0:0.00}", 123.4567));
Преобразование строки в число
Преобразовать строку в число можно либо с помощью методов класса Convert, либо с помощью методов классов, представляющих числа.
Класс Convert
Класс Convert предоставляет набор статических методов для преобразования в базовые типы данных и обратно. Поддерживается следующий набор типов: SByte, Byte, Int16, Int32, Int64, UInt16, UInt32, UInt64, Single, Double, Decimal, Boolean, Char, DateTime, String.
Приведем несколько примеров:
int n1 = Convert.ToInt32("123"); int n2 = Convert.ToInt32(true); Console.WriteLine($"n1 = {n1}, n2 = {n2}"); int n3 = Convert.ToInt32("0x0d", 16); string s1 = Convert.ToString(n3, 16); Console.WriteLine($"n3 = {n3}, hex format: n3 = 0x{s1}");
При работе с Convert может происходить:
- Успешное преобразование.
- Выброс исключения InvalidCastException, если запрашиваемое преобразование не может быть выполнено. Это может происходить при попытке преобразовать типы Boolean, Double, Decimal, Single, DateTime в Char и обратно, DateTime в не строковый тип и обратно.
- Выброс исключения FormatException, если преобразование завершается с ошибкой.
- Выброс исключения OverflowException, если происходит потеря данных при сужении типа (см. Приведение типов).
Класс Convert позволяет работать с разными системами счисления. Поддерживаются двоичное (2), восьмеричное (8), десятичное (10) и шестнадцатеричное (16) основание:
Console.WriteLine($"From bin: {Convert.ToInt32("1111011", 2)}"); Console.WriteLine($"From oct: {Convert.ToInt32("173", 8)}"); Console.WriteLine($"From hex: {Convert.ToInt32("7b", 16)}");
Методы числовых типов
Другим способом преобразования строки в число является использование методов Parse и TryParse, которые предоставляют числовые типы данных. Метод TryParse пытает преобразовать переданную в него строку в число, если это возможно, то полученное значение присваивается второму аргументу с ключевым словом out и возвращает значение true, в ином случае возвращает значение false.
int value; if(int.TryParse("123", out value)) { Console.WriteLine($"Value: {value}"); } // Будет выведено: Value: 123 if(int.TryParse("hello", out value)) { Console.WriteLine($"Value: {value}"); } else { Console.WriteLine("Can't parse string"); } // Будет выведено: Can't parse string
В первом случае преобразование строки “123” в число будет выполнено успешно, во втором нет.
Преобразовать строку в число можно также с помощью метода Parse, если процесс пройдет успешно, то будет возвращено численное значение соответствующего типа, если нет, то будет выброшено исключение ArgumentNullException, ArgumentException, FormatException или OverflowException.
Console.WriteLine(int.Parse("123")); try { int n5 = int.Parse("hello"); } catch(Exception e) { Console.WriteLine(e.Message); }
Тип данных Boolean
Экземпляр типа данных Boolean можно создать через объявление либо получить в результате логической операции.
Логические операторы
C# предоставляет следующий набор логических операторов для работы со значениями типа Boolean:
Название | Обозначение | Пример |
Логическое отрицание | ! | !a |
Логическое И | & | a & b |
Логическое исключающее ИЛИ | ^ | a ^ b |
Логическое ИЛИ | | | a | b |
Условный оператор логического И | && | a && b |
Условный оператор логического ИЛИ | || | a || b |
Операторы &, ^, | всегда обрабатывают оба операнда в выражении, операторы && и || вычисляют правый только в случае, если это необходимо.
Ниже приведены примеры использования логических операторов:
bool b1 = true; bool b2 = !b1; // false bool b3 = true & b1; // true bool b4 = false & b1; // true bool b5 = b1 && b2; // false bool b6 = b1 || b2; // true b1 &= true; // true b1 |= false; // true