Целые числа или десятичные дроби? Числовой тип Decimal
Вы спросите: зачем использовать типы целых чисел Integer и Long, если типы десятичных дробей Single и Double обеспечивают нам работу как с целыми, так и с дробными числами? Здесь дело в наличии или отсутствии абсолютной точности вычислений. При использовании типов десятичных дробей вполне мыслима ситуация, когда дважды два будет не точно 4, а, скажем, 4.00000000000381. Связано это с особенностями представления десятичных дробей в компьютерах. В большинстве реальных задач такая маленькая погрешность несущественна, однако существуют задачи, где точность нужна абсолютная. При использовании же типов целых чисел VB присматривает за тем, чтобы все числа и результаты были абсолютно точными целыми числами. При сложении, вычитании и умножении это, сами понимаете, не проблема, а вот при делении компьютеру приходится округлять.
Совет: Если вы уверены, что переменная всегда должна быть целым числом и никогда дробным, объявляйте ее целым типом, если есть хоть маленькое сомнение – дробным.
Однако абсолютная точность бывает нужна и при работе с дробными числами. Например, в финансовых расчетах доллары или рубли – это целая часть десятичной дроби, а центы или копейки – сотые доли этой дроби. Ошибаться здесь нельзя ни на копейку. В этой ситуации идеально подходит тип Decimal. Этот тип хоть и имеет дробную часть, сложение и вычитание дробных чисел выполняет абсолютно точно. Количество значащих цифр у него рекордное (см. таблицу в 5.4.2).
Попробуем теперь проверить работу типа Decimal.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim a, b, c As Decimal
a = 1234567890123456789
b = 0.0098765432109
c = a + b
WriteLine(a) : WriteLine(b) : WriteLine(c)
End Sub
Запустите проект. Вот результат:
0,0098765432109
1234567890123456789,0098765432
Как видите, VB, как и положено, обрезал сумму до 29 значащих цифр.
Символы типа.Обратите внимание, что значения переменным а и b я дал не длинные, тип Decimal может проглотить и гораздо длиннее. Попробуем добавить цифр к литералу переменной а. VB подчеркивает литерал и выдает подсказку об ошибке, в которой мы видим слово overflow. В чем дело? Снова укоротим литерал, поставим на него курсор мыши и увидим подсказку «Long». Почему? Ведь переменная a имеет тип Decimal! Дело в том, что VB без явного приказа человека не хочет причислять литералы к типу Decimal. А приказ такой – буква D в конце литерала:
a = 12345678901234567890123456789D
Теперь все в порядке. Буква называется символом типа(Literal type character).
Если вы захотите заставить VB причислить литерал к определенному числовому типу, то вот вам соответствующие символы типов:
Short | Integer | Long | Single | Double | Decimal |
S | I | L | F | R | D |
Преобразование типов
Трудности. Итак, мы видим, что когда дело касается типов, VB проявляет некоторую придирчивость. Например, как мы только что видели, он не разрешает литералам без специальных символов иметь любой тип. Или требует, чтобы результат сложения, вычитания или умножения чисел определенного целочисленного типа умещался в этот же тип. Например, после запуска процедуры
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim a As Byte = 200
Dim b As Byte = 100
Debug.WriteLine(a - b)
Debug.WriteLine(a + b)
End Sub
будет выдана ошибка переполнения, так как 200+100 больше 255 – предельного значения для типа Byte.
Вы столкнетесь также с придирчивыми функциями, требующими себе аргументов определенного типа и жалующимися, если они имеют другой тип.
Во всех этих случаях перед вами встанет вопрос: уступать требованиям VB или не уступать. Казалось бы: чего проще! – уступи и дело с концом. Например, я советовал для предотвращения переполнения объявлять все переменные, участвующие в вычислениях, самым вместительным типом из тех, что могут понадобиться для результатов вычислений. И это хороший совет.
Еще один совет: если ничего не помогает, откажитесь от объявления типа у переменных, являющихся причиной придирок, смирившись с отрицательными последствиями неопределенности типа. То есть просто пишите Dim a.
И с придирчивой функцией тоже нетрудно согласиться и объявить переменную так, как она требует.
Но не всегда эти уступки бывают программисту удобны. Мы еще столкнемся с конкретными случаями такого неудобства. И поэтому сейчас я покажу вам, как сделать так, чтобы и волки были сыты, и овцы целы.
Функции преобразования типов. Существуют специальные функции, назначение которых – преобразовывать свой аргумент к определенному типу. Например, функция CInt преобразовывает свой аргумент к типу Integer. Поэтому оператор
Debug.WriteLine(CInt(2.4 + 10))
напечатает 12, а не 12,4.
Пусть в предыдущем примере про 200+100 мы не хотим менять тип переменных Byte на более вместительный Short. Как же тогда избавиться от переполнения? Применить CShort – функцию преобразования своего аргумента в тип Short. Вот так:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim a As Byte = 200
Dim b As Byte = 100
Debug.WriteLine(a - b)
Debug.WriteLine(CShort(a) + CShort(b))
End Sub
Здесь функция CShort преобразует перед сложением значения переменных a и b из типа Byte в тип Short. Не переменные, а их значения, и не везде, а только в данном месте. Сами переменные как были, так и остались типа Byte, и их значения в других местах программы (там, где вычитание) тоже остались типа Byte.
Преобразование состоялось и сложение поэтому выполнялось над числами типа Short и результат (300) обязан был умещаться именно в этот тип, куда он свободно и уместился.
Вот таблица функций преобразования типов, куда я для справки включил и функции преобразования в нечисловые типы:
Функция | Преобразует в тип |
CByte | Byte |
CShort | Short |
CInt | Integer |
CLng | Long |
CSng | Single |
CDbl | Double |
CDec | Decimal |
CChar | Char |
CStr | String |
CBool | Boolean |
CDate | Date |
CObj | Object |
Неявное преобразование типов. При выполнении арифметических действий (и в других случаях) VB часто незаметно для нас и сам преобразовывает числа из одного типа в другой. Например, вычисляя выражение 5+2.8, VB преобразует число 5 из типа Integer в тип Double, после чего складывает числа и получает результат 7.8 типа Double.
Вообще,
выполняя арифметические действия над числами разных типов, VB преобразует их к единому типу, который вмещал бы типы обоих чисел, после чего присматривает, чтобы результат тоже умещался в этом типе.
Вот список числовых типов в порядке возрастания вместительности:
Byte, Short, Integer, Long, Single, Double, Decimal
Пример: при выполнении фрагмента
Dim a As Short = 32700
Dim b As Byte = 100
Debug.WriteLine(a + b)
VB преобразует число 100 в тип Short, а затем выдаст переполнение, так как результат сложения не умещается в тип Short.
При делении целочисленных типов VB преобразует делимое и делитель в основном к типу Double и результат получается тоже типа Double.
Например, при делении 2 на 3 VB видит, что результат не получится целым, поэтому он сначала преобразует оба целых числа в тип Double и результат получает того же типа.
Существуют определенные правила неявного преобразования типов, в которые мы не будем подробно вдаваться.
Тип арифметического выражения. Пусть VB вычисляет выражение a+b*c. Спрашивается, какой тип будет у вычисленного выражения? Рассуждаем по цепочке. Сначала VB выполняет умножение b*c и тип произведения определяет по только что приведенному правилу. Затем, имея уже произведение и зная его тип, VB прибавляет его к a, определяя тип суммы опять по тому же правилу.
Так, по цепочке выполняемых действий, VB определяет тип любого самого длинного выражения.
Форматированиечисел
Одни и те же значения могут выглядеть по-разному. Например, в школьной тетрадке одну и ту же дату вы можете записать так – 25.12.03 и так – 25 декабря 2003 года. Одно и то же число вы можете записать так – 500 и так – 5*102. Что касается VB, то он выбирает внешний вид данных, исходя из своих соображений, которые не всегда совпадают с нашими желаниями. В этом случае возникает необходимость строго указать компьютеру, в каком виде (формате) мы хотим лицезреть то или иное значение.
Взгляните на такую программу:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim a As Double = 1230000000000000000
WriteLine(a)
End Sub
Запустите проект. Вот результат:
1,23E+18
Что это значит? Это значит, что VB не любит слишком длинных чисел и представляет их вам не в привычном для обычного человека, а в так называемом экспоненциальномилинаучномформате. Число 1,23E+18 это то же самое число 1230000000000000000, только выглядящее по-другому. Конструкция E+18 означает просто умножение на 1018. Таким образом, 1,23E+18 означает 1,23*1018. По-другому, вам нужно передвинуть запятую на 18 позиций направо – и получится нормальное число.
А теперь взгляните на такую программу:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim a As Double = 0.00000000000654
WriteLine(a)
End Sub
Результат:
6,54E-12
Конструкция E-12 означает просто умножение на 10-12 или, что то же, деление на 1012. Таким образом, 6,54E-12 означает 6,54*10-12. По-другому, вам нужно передвинуть запятую на 12 позиций налево – и получится нормальное число.
Если после этих объяснений вы все еще не полюбили экспоненциальный формат, вы можете приказать компьютеру, чтобы он вас не утомлял им, а показывал результаты по-человечески. Для этого в операторе WriteLine(a) нужно использовать функцию форматирования, то есть управления внешним видом. Функция эта называется Format. Для конкретности возьмем первую из наших двух программ, ту, где присутствует большое целое число. Здесь нужно вместо a написать Format(a, "#"). Получится
WriteLine(Format(a, "#"))
Символ # внутри кавычек означает, что вы желаете видеть число в обычном виде и без дробной части. Вот тот же результат в новом формате (проверьте):
А теперь разберем наиболее популярные возможности функции Format по форматированию чисел (а форматировать она может, кстати, данные и других типов). Возможности эти иллюстрирует следующая программа:
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim a As Double = 12345.67890123
Dim b As Double = -0.0729
WriteLine(Format(a, "#"))
WriteLine(Format(a, "0"))
WriteLine(Format(a, "#.###"))
WriteLine(Format(a, "0.000"))
WriteLine(Format(a, "########.##########"))
WriteLine(Format(a, "00000000.00000000000"))
WriteLine(Format(b, "########.##########"))
WriteLine(Format(b, "00000000.00000000000"))
WriteLine(Format(b, "0.##########"))
WriteLine(Format(b, "P"))
WriteLine(Format(a, "E"))
WriteLine(Format(a, "C"))
WriteLine(Format(a, "Ж###У###Ч###К###А####.#К###А###Ш###Т##А#Н#К#А"))
End Sub
Запустите проект. Вот результаты:
12345,679
12345,679
12345,67890123
00012345,67890123000
-,0729
-00000000,07290000000
-0,0729
-7,29%
1,234568E+004
12 345,68р.
ЖУЧК1А2345,6К789А012Ш3ТАНКА
Поясним то, что мы увидели. В скобках функции Format располагаются через запятую два аргумента. Первый аргумент – это то, что мы форматируем – переменная, число, выражение. Второй аргумент – строка в кавычках, при помощи которой мы и управляем внешним видом числа. Символы в кавычках называются символами форматаи спецификаторами формата(мы не будем вдаваться в тонкости, объясняя какие из них какие). Вот пояснение действия этих символов (полный смысл символов # и 0 становится ясен по прочтении нескольких строчек таблицы):
Символы и результаты | Пояснение |
# | Вы желаете видеть число в обычном виде и без дробной части |
Вы желаете видеть число в обычном виде и без дробной части | |
#.### | Вы желаете видеть число в обычном виде. После запятой вы желаете видеть не больше 3 знаков, целая часть дроби все равно будет показана полностью |
12345,679 | |
0.000 | Вы желаете видеть число в обычном виде. После запятой вы желаете видеть ровно 3 знака, целая часть дроби все равно будет показана полностью |
12345,679 | |
########.########## | После запятой вы желаете видеть не больше 10 знаков. Если целая часть состоит из одного нуля, то ноль перед запятой показан не будет |
12345,67890123 ‑,0729 | |
00000000.00000000000 | После запятой вы желаете видеть ровно 10 знаков, недостающие места будут заполнены нулями. Если целая часть дроби короче 8 цифр, недостающие места будут заполнены нулями. |
00012345,67890123000 ‑00000000,07290000000 | |
0.########## | Слева от запятой действуют «законы 0», а справа – «законы #» |
‑0,0729 | |
P | Число переводится в проценты умножением на 100 и добавлением знака % |
-7,29% | |
E | Число показывается в экспоненциальном виде |
1,234568E+004 | |
C | Число показывается в виде валюты страны, на которую настроена Windows |
12 345,68р. | |
Последняя строчка процедуры «с Жучкой и Каштанкой» показывает, что внутрь кавычек мы можем добавлять любые символы и они будут отображены на соответствующих местах. Это открывает перед нами возможности необычного форматирования. Надо только, чтобы эти символы не совпадали со стандартными, а то VB спутается. |
Улучшаем калькулятор. Вот вам и первое улучшение для нашего калькулятора, чтобы он не показывал длинные результаты в экспоненциальном формате. Вместо
Результат.Text = Val(Число1.Text) / Val(Число2.Text)
можете написать
Результат.Text = Format(Val(Число1.Text) / Val(Число2.Text), "0.####################")
Только имейте в виду – если ваш результат будет такой маленький, что двадцати цифр, указанных мной после точки, не хватит, то ничего, кроме нулей, вы и не увидите, а вот экспоненциальный формат покажет вам результат, пусть и непривычный для чтения.