Суббота, 18.05.2024, 15:46
Приветствую Вас Гость | RSS
Форма входа
Поиск
Календарь
«  Май 2024  »
ПнВтСрЧтПтСбВс
  12345
6789101112
13141516171819
20212223242526
2728293031
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

Charles Petzold ".NET Book Zero" Глава 6. Основные типы данных

Как вы видели C# поддерживает типы данных string, char, int, и double. Эта глава содержит более методичный подход к ним и другим основным типам данных, поддерживаемых C# и CLR.

Целые

Как C и C++, C# поддерживает целые типы данных называющиеся short, int, и long. Но почти сразу же начинают становиться очевидными различия между C/C++ и C#. Стандарты C и C++ накладывают минимальный размер в битах для трех целочисленных типов данных, а C# точно фиксирует их. В C#, short всегда длинной 16 бит, int – 32 бита, и long – 64 бита.

Short, int, и long – целые со знаком хранящиеся в стандартных двух дополнительных форматах. Short может принимать значения от -32768 до 32767, int от -2147483648 до 2147483647, и long от -9.223.372.036.854.775.808 до 9.223.372.036.854.775.807. Внутри метода, как и в методе Main, вы можете объявить int с именем i (например), при помощи оператора объявления:

int i; 

Это объявление по существу выделяет 4 байта памяти в стеке для хранения значения i. Тем не менее, на данный момент, i не инициализирован, и любая попытка обратиться к его значению будет вызывать у C# компилятора сообщение об ошибке. Например, компилятор C# не позволит выполнить этот код:

int i; 
Console.WriteLine(i); 

После того как i объявлено, вы можете присвоить ему значение:

i = 545; 

Или, вы можете инициализировать i в операторе объявления:

int i = 545; 

В любом случае, число 545 целочисленный литерал, который обычно считается типом int, за исключением того, что C# компилятор будет немного нарушать правило, если вы назначите литерал – типа short, например:

short s = 545; 

Вы можете присвоить значение типа int переменной типа long:

long l = i; 

Это известно, как – неявное преобразование. Int преобразуется в long без проблем. Кроме того, вы можете присваивать short переменным int или long без преобразования. Тем не менее, присваивание в обратную сторону будет проблематичным, потому что значение long может превысить максимальное значение int, поэтому – необходимо явное приведение:

i = (int)l; 

Хотя, очевидно, что, в данном конкретном примере нет никакой проблемы, в большинстве случаев значение long будет сокращено до числа, укладывающегося в int. Кроме того, вы должны использовать явное приведение для присвоения long переменной short, или int в short.

C# также поддерживает беззнаковые целочисленные типы данных, но слово "unsigned" не является ключевым, как в C и C++. Три беззнаковых целых типа данных называются ushort, uint, и ulong. Ushort располагается в диапазоне от 0 через 65 535; uint от 0 до 4294967295, а ulong от 0 до 18.446.744.073.709.551.615.

Вы можете назначить ushort к uint или ulong без явного приведения типов, или uint к ulong без явного приведения, потому что ничего плохого не случится. Вы также можете присвоить ushort к int, или uint к long без явного приведения. Всё потому что, получатель может вместить все возможные значения исходной переменной. А явное приведение требуется везде, где значение может не сохраниться. Например, присваивание int в uint, или uint в int всегда требует явного приведения.

Числовые переменные, как правило, предполагают тип int, за исключением чисел слишком больших для int, в случае которых, C# компилятор предполагает (по нарастающей), что это на самом деле uint, long, или ulong. Вы можете быть более конкретным, применяя суффикс U или u (для беззнаковых) или L или l (для long) или комбинации U, u, L или l.

Вы можете использовать шестнадцатеричные числа, продолжая 0x или 0X цифрами:

int hex = 0x4AbC; 

В C#, вы не можете пользоваться восьмеричными или двоичными переменными. long и ulong типы имеют ширину 64-бит, типы int и uint – 32-битную ширину, а типы short и ushort – 16-бит. C# также поддерживает два 8-битных целочисленных типа, но называющихся на оборот. Byte – беззнаковый 8-битный тип, способный хранить значения от 0 до 255. Sbyte является 8-битным типом со знаком, способным хранить значения от -128 до +127.

Неявные преобразования разрешены для преобразования byte в short, ushort, int, uint, long, и ulong; и sbyte в short, int, и long. Программа также может объявлять константы:

const int a = 17; 

Типу данных должно предшествовать ключевое слово const, и константа должна быть инициализирована в операторе объявления. Вы можете объявить константу либо локально в методе или в виде поля. Если вы объявляете константу в качестве поля, ненужно использовать ключевое слово static вместе с const. Константы неявно являются статическими, то есть, они не зависят от экземпляра. Поле Math.PI является константой. Константы иногда рассматривают в качестве переменных, которые не могут быть изменены, но на самом деле они сильно отличаются от переменных. Константы не занимают память во время выполнения. Они всегда определяются во время компиляции и подставляются в коде. Например, если константа была определена, как показано выше, оператор

int b = a + 18; 

был бы эквивалентен:

int b = 17 + 18; 

Инициализированное значение константы должно быть доступно во время компиляции. Если программа делит число на ноль, программа выкинет исключение DivideByZeroException. Если программа содержит выражение, которое делит число на 0 или на константу со значением 0, компилятор C# привлекает внимание флагом, о том, что здесь – ошибка компиляции.

По умолчанию, исключение не возникает, когда происходит переполнение или опустошение целого.

Например:

int i = 50000; 
Console.WriteLine(i * i); 

Это выражение будет выводить значение – 1794967296 (а если вы не знаете, почему оно отрицательное, вы можете обратиться к главе 13 моей книги "Код: Тайный язык информатики" или к восьмой главе книги "Программирование в тональности C#"),

Если вы предпочитаете, чтобы ваша целочисленная арифметика проверялась на переполнение и опустошение – возможно только во время отладки программ – вы можете установить соответствующий переключатель компилятора. При компиляции в командной строке, используйте переключатель:

/checked+ 

В Visual Studio, перейдите в Свойства проекта, выберите вкладку "Построить", нажмите кнопку "Дополнительно" и установите флажок: Проверять арифметическое переполнение / опустошение. Теперь любое переполнение или опустошение выбрасывает OverflowException.

Вы также можете выполнить проверку переполнения/опустошения отдельных выражений или групп используя ключевое слово – checked. Например, это утверждение выполняет проверку только на одного выражения:

B = checked(5 * A); 

Если вы включили проверку переполнения/опустошения с помощью переключателя компилятора, вы можете выключить его для отдельных выражений при помощи ключевого слова – unchecked. Например, вам необходимо чтобы определенное выражение не проверялось, потому что вы используете переполнение намеренно, тогда:

B = unchecked(5 * A);

Вы также можете выполнить проверку переполнения/опустошения для конкретного блока операторов:

checked 
{ 
A += 1000000; 
B = B * A; 
} 

Кроме того, вы можете отключить проверку для конкретного блока операторов. Просто имейте в виду, что наиболее эффективным кодом является тот, в котором не происходит проверка на переполнение/опустошение во время выполнения.

Целые и .NET Framework

Ключевые слова byte, sbyte, short, ushort, int, uint, long и ulong – являются псевдонимами для структур в библиотеке классов .NET Framework. Например, если вы посмотрите в документацию пространства имен System, вы найдете структуру под названием Int32. Это структура, которой соответствует беззнаковый 32-битный int. В качестве альтернативы вы можете определить переменную int с именем i следующим образом:

System.Int32 i; 

Или, если у вас есть, директива using для пространства имен System, вы можете написать так:

Int32 i; 

Где бы вы ни использовали int, вы можете альтернативно использовать System.Int32 или (если есть директива using) Int32. Джефф Рихтер постоянно рекомендует C# программистам использовать имена структур .NET. (См "CLR via C#", стр 119.) В дальнейшем, вы узнаете, что иногда необходимо ссылаться на типы по имени структуры, и это будет тем легче, чем больше вы привыкнете к использованию этих имён.

Следующая таблица показывает восемь целых типов данных и соответствующие им структуры в .NET Framework:

C# Alias

.NET Structure

Description

CLS Compliant?

sbyte

System.SByte

Signed 8-bit integer

No

byte

System.Byte

Unsigned 8-bit integer

Yes

short

System.Int16

Signed 16-bit integer

Yes

ushort

System.UInt16

Unsigned 16-bit integer

No

int

System.Int32

Signed 32-bit integer

Yes

uint

System.UInt32

Unsigned 32-bit integer

No

long

System.Int64

Signed 64-bit integer

Yes

ulong

System.UInt64

Unsigned 64-bit integer

No

 

В последнем столбце, Я указал, когда конкретный тип данных соответствует спецификации Common Language (CLS). CLS определяет минимальный стандарт для языков программирования, используемых в сочетании с .NET. CLS не требует от языков программирования реализации знаковых 8-битных целых чисел, или беззнаковых 16, 32 или 64 битных целых. Это означает, что конкретный язык программирования может быть совместимым с CLS без реализации этих типов данных.

Однако, нет причин, по которым вы не должны использовать эти типы данных в ваших программах на C#. Тем не менее, если вы пишете код для динамической библиотеки, то вы, безусловно, должны избегать таких типов данных, для определения открытых полей, или в качестве параметров, или возвращаемых значений из открытых методов, или как типы открытых свойств. Методы в DLL не должны иметь uint аргументы или возвращать uint значения, потому что это, не даст использовать метод в программе, написанной на языке, который не реализует тип данных UInt32.

Есть ли, от определения этих типов данных в качестве структур .NET Framework, какая-либо выгода?

Да. Например, метод ToString определённый в структуре Int32 больше приспособлен для форматирования чисел. Кроме того, каждая из структур числовых типов данных определяет поля общедоступные константы с именами MinValue и MaxValue. Структура Int16 определяет:

public const short MinValue = -32768; 
public const short MaxValue = 32767; 

Вы можете обратиться к этим полям в программе, предварив их именем структуры Int16:

Console.WriteLine(Int16.MinValue); 

Если у вас не было директивы using для пространства имен System, это объявление должно было бы выглядеть:

System.Console.WriteLine(System.Int16.MinValue);

Вы можете использовать полное имя, даже если у вас есть директива using для пространства имен System. А также, можно предварять поля псевдонимом short:

Console.WriteLine(short.MinValue); 

Булевые типы данных

C# определяет тип данных bool, который является псевдоним структуры System.Boolean. Переменная bool может иметь одно из двух возможных значений, которые эквивалентны ключевым словам C# – true (истина) и false (ложно).

Одним из соглашений именования, которое возникло в .NET Framework, является то, что логические методы и свойства часто начинаются со слова "Is". Локальные поля или переменные можно начать в нижнем регистре, вот так:

bool isGreatDay = true; 

Как вы узнаете, в следующей главе, некоторые общие операторы C# возвращают значения bool. Это операторы равенства ( == и != ) и операторы сравнения ( <, >, <= и >=). Вы можете использовать логические операторы ( &, |, и ^ ) и условные операторы ( && и || ) с булевыми значениями. Структура Boolean определяет метод ToString, который отображает слова "True" или "False", и метод Parse, который реализует чувствительные к регистру преобразования строк "true" или "false" в значения bool.

Символы и строковый тип данных

Как C и C++, C# определяет тип данных с именем char, но, строго говоря, char – это не интегральный тип данных в C#. Хотя вы можете легко осуществлять преобразования между char и интегральными типами данных, в C# нет такой вещи, как, char со знаком или беззнаковый char. Тип данных char в C# является псевдонимом структуры System.Char. Программа может задать один буквенный символ, используя одинарные кавычки:

char ch = 'A'; 

Преобразование переменной char в целое число требует явного преобразования:

int i = (char) ch; 

В C# char хранит один символ Unicode, который занимает 16 бит. Таким образом, символьные переменные имеют числовые значения от 0x0000 до 0xFFFF. Как и у строковых литералов, обратная косая черта является управляющим символом. Следующее объявление инициализирует символьную переменную – заглавной греческой буквой омега – " ":

char omega = '\x03A9'; 

Или, вы можете задать знак целым числом:

char omega = (char) 0x03A9; 

Как вы знаете, в C или C++, вы можете использовать функции, объявленные в заголовочном файле ctype.h, чтобы определить, является ли конкретный символ: буквой, цифрой, управляющим символ, или чем-то ещё. В C#, вы используете статические методы, определенные в структуре Char: IsControl, IsSeparator, IsWhiteSpace, IsPunctuation, IsSymbol, IsDigit, IsNumber, IsLetter, IsUpper, IsLower, IsLetterOrDigit, IsSurrogate. Эти методы существуют в двух версиях, первый из них имеет один параметр типа char, а другой – принимает строку и индекс. Вызов:

Char.IsControl(str[index]); 

аналогичен:

Char.IsControl(str, index);

Все эти методы возвращают булевые значения. Тип данных string является псевдонимом класса System.String. string – это коллекция объектов char, следующих один за другим. Свойство Length, определённое классом String, указывает количество символов в строке. А индексатор – указывает на отдельные символы. Класс String определяет множество методов для работы со строками, которые вы можете изучить по своему усмотрению.

Поскольку строки являются неизменными, легко писать код, который выглядит достаточно безобидным, но при этом является – очень неэффективным. См. Главу 26 с описанием проблем и их решений.

Числа с плавающей запятой

Как C и C++, C# поддерживает типы данных c плавающей точкой под названием float и double, одинарной точности и двойной точности которые соответствуют требованиям, указанными в ANSI/IEEI Std 754-1985, в "Стандарте IEEE на двоичную арифметику с плавающей точкой".

double num1 = 576.34; 
float num2 = 34.89f; 

По умолчанию, компилятор C# будет предполагать, что любая числовая переменная с десятичной точкой является double. Если вы хотите, чтобы вместо этого было float, необходимо использовать F или f-суффикс, как показано во втором операторе объявления. Вы можете использовать D или d-суффикс, чтобы явно указать double. Во всяком случае, после десятичной точки, должна быть указана хотя бы одна цифра. Вы можете использовать E заглавную или в нижнем регистре, чтобы указать степень:

double num1 = 5.7634E2; 
float num2 = 3489.0e-2f; 

Тип float псевдоним структуры System.Single, а тип double псевдоним структуры System.Double. (Обратите внимание, что в С# тип данных float, но структура называется Single.) Значение с плавающей точкой состоит из 24-битной целой мантиссы и 8-битной степени. Точность составляет примерно семь десятичных цифр. Значения находятся в диапазоне от

-3.402823 х 1038

до:

3.402823 х 1038

Наименьшее возможное значение с плавающей точкой, больше чем 0:

1.401298 х 10-45

Вы можете получить эти три значения от постоянных полей MinValue, MaxValue, и Epsilon, определенных в структуре Single.

Значение double состоит из 53-битной целой мантиссы и 11-битной степени. Точность составляет примерно от 15 до 16 десятичных цифр. Значения находятся в диапазоне от

-1.79769313486232 х 10308 

до:

1.79769313486232 х 10308 

Наименьшее возможное значение double больше, чем 0:

4.9465645841247 х 10-324 

Поля MinValue, MaxValue, и Epsilon также определены в структуре Double.

Неявное приведение разрешается из float в double, и из любого интегрального типа во float или в double. Явное приведение требуется из double в float, или из double или float в любой интегральный тип. В арифметических выражениях, в которых смешаны типы с плавающей точкой и целые типы, для расчета целые преобразуются в тип float. Если эти выражения содержат double, любые целые числа или значения с плавающей точкой будут конвертированы в double.

Вот код, который делит число с плавающей точкой на ноль:

double d1 = 1; 
double d2 = 0; 
double d3 = d1 / d2; 

Если бы это были целые, выбросилось бы DivideByZeroException. Но это число IEEE с плавающей точкой. Исключение не выбрасывается. Операции с плавающей точкой не вызывают исключений в C#. Вместо этого, в данном случае, d3 приобретает особое значение. Если вы используете Console.WriteLine для отображения d3, оно отобразит слово

Infinity 

Если вы измените инициализацию d1 на -1, Console.WriteLine отобразит сообщение:

-Infinity 

В стандарте IEEE, положительная бесконечность и отрицательная бесконечность – допустимые значения чисел с плавающей точкой. Вы даже можете выполнять арифметические действия с бесконечными значениями. Например, выражение:

1 / d3 

эквивалентно 0.

Если вы измените инициализацию d1 в предыдущем коде на 0, то d3 будет равняться значению, известному как – Not a Number (не число), сокращенно – NaN, и произносимое как – "нан" Вот как Console.WriteLine отображает NaN:

NaN 

Можно создать NaN добавлением положительной бесконечности к отрицательной бесконечности или некоторыми другими вычислениями.

Обе структуры Single и Double имеют статические методы, названные IsInfinity, IsPositiveInfinity, IsNegativeInfinity и IsNaN, чтобы определить, является ли конкретное значение float или double бесконечностью или NaN. Эти методы принимают аргумент с плавающей точкой и возвращают логическое значение. Например:

Double.IsInfinity(d) 

возвращает true, если d является положительной бесконечностью или отрицательной бесконечностью. Структуры Single и Double также имеют постоянные поля, названные PositiveInfinity, NegativeInfinity и NaN для представления этих значений. Эти значения соответствуют определенным битовым комбинациям в стандарте IEEE. Тем не менее, эти битовые комбинации не являются уникальными, так что, не рекомендуется использовать эти поля для операций сравнения. Например, даже если d является NaN, операция

d == Double.NaN 

вернет false, если битовый образ d не точно соответствуют тому, что в Double.NaN. Используйте статические методы для определения статуса отдельных чисел:

Double.IsNaN(d) 

Много путаницы окружает операцию остаток, от чисел с плавающей точкой. Остаток в C# или оператор модуль (%) определяется для всех числовых типов. (В C, оператор модуль не определен для float и double, и вместо этого должна использоваться функция fmod.) Вот выражение C# использующее числа double с оператором остатка:

result = dividend % divisor; 

Знак результата (result) совпадает со знаком делимого (dividend), и результат может быть вычислен по формуле

result = dividend – n * divisor 

где n максимально возможное целое число, меньшее или равное делимому разделённому на делитель (divisor). Например, выражение:

4.5 % 1.25 

равно 0,75. (Выражение 4.5 / 1.25 равно 3.6, так что n равно 3. Число 4,5 минус (3 раза по 1,25) равно 0,75.) Стандарт IEEE определяет остаток немного по-другому, где число n ближе к делимому / делитель. Вы можете получить остаток в соответствии с IEEE стандартом, используя статический метод Math.IEEERemainder. Выражение

Math.IEEERemainder(4.5, 1.25) 

равно -0,5. Это потому, что 4,5 / 1,25 равно 3,6, а ближайшее целое число, до 3,6 4. При n равном 4, число 4,5 минус (4 раза 1,25) равно -0,5.

Десятичный тип данных – Decimal

C# также определяет десятичный тип данных, который предоставляет точность до 28-го десятичного знака. Тип decimal подходит для хранения и вычисления чисел с фиксированным количеством десятичных знаков, например, деньги и процентные ставки. В моей книге "Программирование в тональности C#", я намеренно раскрыл тип decimal до типов с плавающей точкой. Я думаю, что новым программистам – важно использовать decimal, для большинства приложений, связанных с нецелыми типами данных, в частности, когда расчеты касаются денег. С и С++ языки не были разработаны в традиции, которая высоко ценила адекватные инструменты для финансовых приложений. Тип decimal является попыткой исправить эту исторически сложившуюся недостачу.

Тип данных decimal является псевдонимом структуры System.Decimal. Для decimal больше, чем для любого из других числовых типов данных, эта структура имеет жизненно важное значение, потому что decimal не поддерживается Common Intermediate Language. Что это значит? CIL поддерживает целочисленные типы и два типа с плавающей точкой напрямую, но не тип decimal. Когда вы пишете на C# код для умножения двух значений double, например, C# компилятор генерирует промежуточный язык, помещая два значения в стек, сопроводив их инструкцией CIL mul. Во время выполнения, этот промежуточный язык преобразуется в машинный код, который использует математический сопроцессор.

Но для типа decimal нет CIL mul инструкции. Вместо этого, decimal поддерживается почти исключительно за счет структуры System.Decimal. Когда вы пишете на C# код для умножения двух десятичных чисел, умножение фактически осуществляется методом op_Multiply, определяемом структурой Decimal. (Имя этого метода ссылается на перегруженный оператор умножения, определенном в классе Decimal. Я обсужу перегрузку операторов в главе 20.)

Хотя CIL не поддерживает напрямую десятичного типа данных, тем не менее, он является частью спецификации Common Language. Язык .NET должен поддерживать структуру Decimal, но эта поддержка может быть довольно минимальной. В C#, поддержка осуществлена чуть более, чем просто распознавание десятичных литералов, которые обозначаются суффиксом m или M:

decimal m = 55.23m; 

Оставленная в стороне m приведет к ошибке компиляции. Переменная будет считаться double, и не будет неявного преобразования между типами с плавающей точкой и decimal. Расчеты с использованием decimal не могут управляться с помощью ключевых слов checked и unchecked или соответствующего параметра компилятора. Расчеты, которые приводят к переполнению или опустошению всегда выбрасывают OverflowException.

Тип decimal использует 16 байт (128 бит), чтобы хранить каждое значение, которое в два раза больше, в битах, чем double. 128 бит разделяются на 96-битную целую часть, 1-бит знака, и коэффициент масштабирования, который может варьироваться от 0 до 28 бит. (Двадцать шесть бит не используется.) Математически, коэффициент масштабирования – отрицательная степень 10 и указывает количество знаков после запятой в числе.

Не путайте тип decimal с двоично-десятичным типом (binary-coded decimal (BCD)), который можно найти в некоторых языках программирования. Тип BCD хранит каждую десятичную цифру, используя 4 бит. decimal хранит всё число в двоичном виде. Например, decimal равное 12,34 хранится в виде целого числа (или 0x4D2 десятичного 1234) с коэффициентом масштабирования 2, которое обозначает умножение 10-2. BCD кодирование 12,34 сохранило бы номер как 0x1234. Пока, число имеет 28 целых десятичных цифр (или меньше) и 28 знаков после запятой (или меньше), тип данных decimal представляет точное число. В отличие от чисел с плавающей точкой! Если вы определите число с плавающей точкой, равное 12.34, оно по существу сохраняется как значение 0xC570A4 (или 12939428), разделённое на 0x100000 (или 1048576), которое является только приблизительно равным 12.34. Даже если вы определите double равное 12.34, оно сохранится как 0x18AE147AE147AE (или 6,946,802,425,218,990), разделенное на 0x2000000000000 (или 562,949,953,421,312), что опять же только приблизительно равно 12,34.

И вот почему вы должны использовать decimal при выполнении расчётов, в которых вы не хотите, чтобы копейки таинственно возникали и исчезали. Типы данных с плавающей точкой являются сильно значимыми для научных и инженерных приложений, но часто нежелательны для финансовых. Неявные преобразования позволены из всех целочисленных типов в decimal, и легко понять, почему. Явные приведения типов являются обязательными для преобразований в обратную сторону, и в результате будет выброшено OverflowException, если десятичное число слишком велико, чтобы поместиться в конечном интегральном типе. Явные приведения типов требуются от типов с плавающей точкой в десятичное, потому что экспоненты плавающей точки не позволяют значениям представляться в decimal. Явные приведения типов также необходимы для преобразования из decimal в типы с плавающей точкой, поскольку decimal обеспечивает большую точность.

Тип decimal также включает в себя конструкторы, которые принимают целые типы данных или с плавающей точкой. Это необходимо в основном для языков, которые явно не поддерживают decimal.

Если вы хотите немного изучить decimal, вы можете воспользоваться другим конструктором, который позволит вам собрать decimal из его составных частей:

decimal m = new decimal(low, middle, high, isNegative, scale); 

Первые три аргумента определяется как int, но обрабатываются так, как если бы они были целыми числами без знака. (Если они были определены как беззнаковые целые числа, этот конструктор не был бы CLS-совместимым.) Три 32-разрядных значения становятся 96-битной основной частью decimal. Булевый параметр IsNegative указывает – является ли число отрицательным. Аргумент scale может варьироваться от 0 до 28, указывая количество знаков после запятой.

Выражение

new decimal(1234567, 0, 0, false, 5) 

создает десятичное число 12,34567. Наибольшее положительное десятичное число

new decimal(-1, -1, -1, false, 0) 

или 79.228.162.514.264.337.593.543.950.335, который можно получить из поля Decimal.MaxValue. Наименьшее десятичное число ближайшее к 0 является

new decimal(1, 0, 0, false, 28) 

или 0.0000000000000000000000000001 или 1 . 10-28. Если вы разделите это число на 2 в C# программе, результат будет – 0.

Кроме того, можно получить биты, используемые для хранения значений decimal, используя статический метод GetBits. Этот метод возвращает массив из четырех чисел. Чтобы получить четыре целочисленные значения, которые составляют decimal, необходимо объявить массив типа int и вызвать Decimal.GetBits с десятичной в аргументе:

int[] A = Decimal.GetBits(m); 

Первый, второй, и третий элементы массива (то есть, [0], [1], и [2]) являются младшим, средним и старшим компонентом 96-разрядного целого числа без знака.

Четвертый элемент содержит знак и коэффициент масштабирования. Биты с 0 по 15 равны 0; биты с 16 по 23 содержат коэффициент масштабирования между 0 и 28; биты с 24 по 30 являются 0; а 31-й бит содержит 0 для положительных и 1 для отрицательных. Другими словами, если A[3] является отрицательным, десятичное число является отрицательным. Коэффициент масштабирования:

(A[3] >> 16) & 0xFF 

Почти все, кто активно работает с плавающей точкой могут вспомнить случаи, в которых рассчитывающееся число, которое должно было быть 4,55 (например), часто хранятся в виде 4,549999 или 4,550001. Десятичное представление работает намного лучше. Предположим, m1 определяется следующим образом:

decimal m1 = 12.34m; 

Внутренне m1 имеет целую часть 1234 и коэффициент масштабирования 2. Предположим, м2, определяется так:

decimal m2 = 56.789m; 

Целая часть 56789 и коэффициент масштабирования 3. Теперь сложим эти два числа:

decimal m3 = m1 + m2; 

Концептуально, целая часть m1 умножается на 10 (чтобы получить 12 340), а коэффициент масштабирования устанавливается до 3. Теперь целые части могут быть просуммированы напрямую: 12340 плюс 56789 равно 69129 с коэффициентом масштабирования 3. Фактически это число 69,129. Все точно.

Теперь умножьте два числа:

decimal m4 = m1 * m2; 

Две составные части умножаются (1234 раз по 56789 равно 70776626), и добавляем коэффициент масштабирования (2 плюс 3 равно 5). Фактическим числовым результатом будет 700,77626. Опять же, вычисление является точным. При делении ... ну, деление не является точным независимо от того, как вы его выполняете. Но в большинстве случаев, при использовании десятичной, вы можете иметь гораздо большую уверенность в правильности и точности результатов.

Класс Math

Класс Math в пространстве имен System состоит целиком из коллекции статических методов и двух постоянных полей. Эти два поля типа double называются PI и E.Math.PI является отношение длины окружности к ее диаметру, или 3,14159265358979. Math.E – это предел при n, стремящемся к бесконечности, или 2.71828182845905.

(1+1/n)n

Большинство методов в классе Math определены только для значений double. Тем не менее, некоторые методы определены также, для целых и десятичных значений. Методы Max и Min принимают два аргумента одинакового числового типа и возвращают максимальное или минимальное значение из этих двух чисел, соответственно. Методы Abs и Sign определены для типов с плавающей точкой, для десятичного типа, а также для целых типов со знаком. Метод Abs возвращает абсолютное значение аргумента. Метод Sign возвращает int: 1 – если аргумент положителен, и -1 – если аргумент отрицательный, и 0 – если аргумент равен 0. Метод Abs единственный метод класса Math, который может вызвать исключение, да и то лишь для целых аргументов, и только для одного конкретного значения каждого целого типа, а именно, значению равному полю MinValue. Вызов метода:

short s = Math.Abs(Int16.MinValue); 

выбрасывает OverflowException, потому что Int16.MinValue равно -32768, а 32768 не может быть представлено типом short.

Методы BigMul и DivRem появились в .NET 2.0 и определены для целых чисел. BigMul принимает два аргумента типа int и возвращает long:

long l = Math.BigMul(i1, i1); 

Вы можете получить тот же результат, если вы явно приведёте один из аргументов к типу long:

long l = (long)i1 * i2; 

Метод DivRem определён и для int, и для long. В обоих случаях, он имеет три аргумента и одно возвращаемое значение того же типа. Возвращаемое значение является целым от деления первых двух аргументов. Третий аргумент принимает остаток. Например, если все переменные – a, b, c, и d определены как int (или все определены как long), то вы можете вызвать DivRem следующим образом:

c = Math.DivRem(a, b, out d); 

Переменные a и b должны быть инициализированы перед вызовом метода, а d не обязательно. Переменная с принимает результат целочисленного деления, а d получает остаток. Обратите внимание на ключевое слово out, которое указывает, что d передаётся в метод по ссылке, а затем устанавливается методом. Более подробно, я расскажу в главе 11. Метод DivRem функционально идентичен коду:

c = a / b; 
d = a % b; 

Методы Floor и Ceiling определены только для аргументов типа double. Floor возвращает наибольшее целое число, меньшее или равное аргументу. Ceiling возвращает наименьшее целое число, большее или равное аргументу. Вызов

Math.Floor(3.5) 

возвращает 3, а

Math.Ceiling(3.5) 

возвращает 4. Те же правила применяются и к отрицательным числам. Вызов:

Math.Floor(-3.5) 

возвращает –4, а

Math.Ceiling(-3.5) 

возвращает -3.

Метод Floor возвращает ближайшее целое число в направлении – минус бесконечность, и вот почему он иногда также известен как – "округление в сторону отрицательной бесконечности"; аналогично, Ceiling иногда называют – "округление в сторону положительной бесконечности".

Также, число можно округлить в сторону 0, чтобы получить ближайшее целое число ближе к 0. Вы можете округлить в сторону 0, при помощи явного приведения к целому. Выражение

(int) 3.5 

возвращает 3, а

(int) -3.5 

возвращает -3.

Округление в сторону 0 широко известно, как – "отбрасывание членов". Метод Round определен как для double, так и для decimal. Версия с одним аргументом возвращает целое число, ближайшее к аргументу. Если аргумент Round по середине, между двумя целыми числами, возвращается значение ближайшего четного числа. Например, вызов

Math.Round(4.5) 

возвращает 4, а

Math.Round(5.5) 

возвращает 6. Хотя возвращаемое значение всегда целое число, тип возвращаемого значения такой же, как и тип аргумента (double или decimal).

Если вы предпочитаете соглашение, в котором числа, заканчивающиеся на 0,5, всегда округляются в большую сторону, добавьте 0,5 к номеру, который вы хотите округлить, и отбросьте остаток. Или же, вы можете использовать одну из новых перегрузок метода Round введенную в .NET 2.0, в которой есть аргумент перечисления:

Math.Round(4.5, MidpointRounding.ToEven) 

возвращает 4, но

Math.Round(4.5, MidpointRounding.AwayFromZero)

возвращает 5. Эти значения влияют на результат, только когда число находится посередине между двумя целыми числами.

При желании вы можете подставить целое число в Round, которое укажет количество десятичных знаков в возвращаемом значении. Например:

Math.Round(5.285, 2) 

возвращает 5,28. В .NET 2.0 и выше вы можете подставить целое и значение перечисления. Вызов

Math.Round(5.285, 2, MidpointRounding.AwayFromZero) 

возвращает 5,29.

Три метода класса Math вызывающие степень. Первый – Pow:

Math.Pow(base, power) 

Метод возвращает:

basepower

Метод возвращает NaN, NegativeInfinity или PositiveInfinity в некоторых случаях. Смотрите документацию для получения подробной информации.

Выражение

Math.Exp(power) 

эквивалентно:

Math.Pow(Math.E, power) 

а выражение

Math.Sqrt(value) 

эквивалентно:

Math.Pow(value, 0.5) 

Класс Math имеет три метода, которые вычисляют логарифмы. Выражение

Math.Log10(value) 

эквивалентно

Math.Log(value, 10) 

и:

Math.Log(value) 

эквивалентно

Math.Log(value, Math.E); 

Три основные тригонометрические функции Math.Sin, Math.Cos, и Math.Tan требуют, чтобы углы быть указаны в радианах. Существует 2 радиана в 360 градусах. Если угол в градусах, вызовите Math.Sin, следующим образом:

Math.Sin(Math.PI * angle / 180) 

Методы Math.Sin и Math.Cos возвращают значения в диапазоне от -1 до +1. В теории, метод Math.Tan должен вернуться бесконечность/2 (90 градусов) и 3/2 (270 градусов), но он возвращает очень большие значения вместо этого. Обратные тригонометрические функции возвращают углы в радианах. Следующая таблица показывает возвращаемые значения для собственных диапазонов аргументов:

Method

Argument

Return Value

Math.Asin(value)

–1 through 1

–π/2 through π/2

Math.Acos(value)

–1 through 1

π through 0

Math.Atan(value)

–∞ through ∞

–π/2 through π/2

Math.Atan(y, x)

–∞ through ∞

–π through π

Чтобы преобразовать возвращаемое значение в градусы, умножьте на 180 и разделите на Пи. Методы Asin и Acos возвращают NaN, если аргумент не в надлежащем диапазоне. Метод Atan2 использует знак из двух аргументов, чтобы определить квадрант угла:

y Argument

x Argument

Return Value

Positive

Positive

0 through π/2

Positive

Negative

π/2 through π

Negative

Negative

–π through –π/2

Negative

Positive

–π/2 through 0

Менее используемые гиперболические тригонометрические функции Math.Sinh, Math.Cosh, и Math.Tanh. Значения угла – в гиперболических радианах.


© Charles Petzold "Copyright MyCorp © 2024
Конструктор сайтов - uCoz