Деление нацело типы. Типы данных в Java Приведение типов Коварное деление нацело. Int, double, char
Скачать 26.84 Kb.
|
Типы данных в Java Приведение типов Коварное деление нацело Мы уже знаем несколько типов данных - int, double, char. Есть еще и другие типы, созданные под конкретные цели. Выражение одного типа можно "приводить" к выражению другого типа. Например, приводя дробные к целым, мы отбрасываем дробную часть. Java также не учитывает дробную часть, когда делит одно целое число на другое. Если важен дробный результат, то следует пользоваться дробным типом. В следующей таблице приведены все основные типы данных Java. Указано, сколько бит выделяется под переменную каждого из типов. Биты – это цифры двоичной системы исчисления. В привычной человеку системе десять цифр, а в двоичной – только две, это 1 и 0. Все числа в машине переводятся в двоичную систему. Если в типе данных 8 бит, то на это число отводится 8 двоичных цифр. Вы не можете записать число из девяти цифр – девятая цифра не поместится в ячейку и реально в числе сохранится не настоящий результат, а совершенно ложный - без одной из цифр. Поэтому у каждого типа данных есть максимальное и минимальное допустимое значение. Они доступны во всех справочниках, я привожу их здесь для общего понимания. Сначала приведем таблицу типов данных для целых чисел - byte, short, char, int, long. Если вы уверены, что вам нужны совсем небольшие числа, то можете сэкономить оперативную память и использовать byte. Если же напротив, вы делаете расчет с числами больше двух миллиардов, то вам нужен long. Поначалу вполне можно ограничиться средним вариантом - привычным int.
Дробные типы данных – float для небольших чисел и double для больших. Например, мы пишем: float a = 2.0f; /* 2.0f – буква f в конце указывает, что 2.0 понимается как float, иначе 2.0 будет воспринято как double и Java выдаст ошибку, потому что, может быть, указанное число не влезает в количество цифр float */ double b = 2.0; // по умолчанию любая дробь – это double, поэтому типы double b и 2.0 совпадают, // и присваивание пройдёт без ошибок. Дроби часто записывают в "научной нотации", например, 15e3 означает 15 умножить на 10 в степени 3, то есть 15000. Первое число 15 называется мантисса, а второе число 3 называется порядок. Порядок может быть отрицательным, например, 15e-3 будет означать 15 поделить на десять в третьей степени, то есть 0.015 (15 тысячных). По сути порядок описывает сколько нулей нужно к числу приписать, или же где в числе поставить точку десятичной дроби. Именно в таком виде дроби хранятся в памяти компьютера.
Есть специальный тип boolean, который служит для хранения результатов логических выражений. Эти выражения мы подробно рассмотрим в следующем разделе, а пока укажу, что их результат всегда либо правда (true), либо ложь (false). То есть логическое выражение или истинно, или ложно. Поэтому в Java мы можем написать: boolean res = true; // переменная res хранит "правду" - true res = false; // теперь res хранит "ложь" - false int x = 10; res = x > 20; // 10 меньше 20, поэтому сравнение x больше 20 дает ложь
Приводим один тип к другому Что если вы хотите переменную или число одного типа записать в переменную другого типа? Если точность наверняка не страдает, то Java разрешит вам это сделать с помощью присваивания. Если же точность может упасть, например, при отбрасывании дробной части, Java просит программиста удостоверить, что он действительно хочет преобразовать один тип в другой. Это делается так: double a = 2.2; // привести дробное a к целому b int b = (int) a; // скобки слева от имени переменной или от какого-либо числового выражения // обозначают "привести это выражение к новому типу, указанному в скобках". Определение. Когда перед переменной или выражением слева в скобках указывается тип, то эта переменная или выражение приводится к данному типу. Может быть понижена точность вычислений. Так программист удостоверяет, что он и в самом деле хочет совершить небезопасную операцию. Еще пример: // привести точное выражение double к менее точному float double a = 2.2 float x = (float) a*10; Изменение типа будет осуществлено только при определённых условиях. Главное правило: если точность не теряется, то преобразование типов пройдет само собой (неявно, то есть скобки с типом можно не указывать), если же точность может пострадать, например, в присваивании long к int, double к int, то нужно привести типы явно. Если может потеряться точность, то необходимо привести типы явно Отдельное правило в Java касается типа boolean - к нему нельзя приводить переменные других типов, а также переменные типа boolean не приводятся к другим типам. Этот тип логический, содержит всего два возможных значения - true, false, поэтому по сути не является числом. Как мы увидим в следующих разделах многие типы никак не приводятся друг к другу, если такое преобразование неадекватно и компилятору не понятно, как его проводить. Например, набор целых чисел (массив) нельзя привести просто к int, то есть как-то превратить набор чисел в одно число. Деление нацело (int на int) - популярный источник ошибок Когда мы используем оператор / для целых чисел (int), то Java считает, что должно снова получиться целое число – дробная часть полностью отбрасывается, например: int x = 1/3; // Будет ровно ноль! Дробная часть отбрасывается в int. // Когда мы пишем числа без десятичной точки, то они сразу считаются как int // 10 - это int // 10.2 - это double // сразу разберем нюансы: int y = 10.2; // ошибка - справа дробное число 10.2 double, нужно явно привести тип к int int z = (int) 10.2; // так правильно, дробная часть 0.2 будет отброшена double u = 100; // сначала вычисляется выражение, здесь это просто число 100 - целое // но чтобы приравнять дробное u к целому 100, делается неявное приведение типа. // Поэтому u как была объявлена дробной double, так и останется ей навсегда. Что же, 1/3 = 0 - это логично, в целых числах не могут храниться дробные и мы вынуждены отбрасывать дробные доли. Но довольно часто это приводит к огромным ошибкам, полностью уничтожающим логику приложения. Например, вы делаете калькулятор людей в Москве, владеющих автомобилем. Вы знаете, что всего есть порядка 15000000 жителей и допустим пользователь-статист ввёл, что треть из них владеют автомобилями. Тогда программа должна посчитать «15 миллионов умножить на одну треть» и распечатать пять миллионов, но легко ошибиться: int x = (1/3) * 15000000; //Будет снова ноль! Дробная часть отбрасывается в int еще при расчете 1/3 /* К слову, если в выражении есть double, то операции с ним уже будут идти как с дробным числом, то есть int на double - это double. Но важно учесть, что компилятор вычисляет выражение слева направо, и если было int на int пока он еще не дошел до дробного числа, то эта часть пройдет в целых числах. Например: */ int y = (1/3) * 15000000.0 ; // справа сначала 1/3 будет 0, на что ни умножай. // при этом мы помножили на дробное double, поэтому int на double будет double // и компилятор скажет - ошибка, ведь чтобы int присвоить к double нужно явно привести тип: int z = (int) ((1/3) * 15000000.0); // теперь ошибки нет, но z по-прежнему равно нулю int u = (int) ((1.0/3) * 15000000); // теперь правильно 1.0/3 это одна треть, а не ноль, // поскольку 1.0 - дробное и стоит в начале выражения, так что и все остальные // операции будут с дробными числами. // Снова явно приводим к int, хотим приравнять int u к double. В этом примере главное (1/3) – это выражение независимо от всего остального понимается Java как целое делить на целое (int на int), а значит вычислится с отбрасыванием дробной части и обратится в ноль. При этом ноль умножить на любое число будет ноль, хотя мы умножили на 15 миллионов. Еще несколько замечаний по этому примеру: int a = 15000000 / 3; // мы сразу получим нужные 5 миллионов, вообще без дробных чисел int b = (int) ( (1/3.0) * 15000000 ); // 3.0 – это дробное, тогда и 1/3.0 – это треть, а не ноль. // Благодаря скобкам уже после расчета всего выражения 5 млн будет приведено к целым, // но оно и так целое, так что мы все посчитали точно. int с = (int) (1.0/3) * 15000000; // будет опять чистый ноль, // так как сначала треть приведется к целому типу и обнулится - при равных приоритетах // без дополнительных скобок выражение вычисляется слева направо, то есть (int) раньше. int d = (1.0/3) * 15000000; // ошибка компиляции, справа дробное, слева целое - // компилятор боится потерять точность без разрешения программиста. Теперь получится 5 миллионов. Представьте, если это был финансовый робот в банке? Он мог взять и обнулить 5 миллионов фунтов стерлингов у вас на счету! Поэтому в таких вычислениях нужно использовать дробные, а лучше ещё более точные специальные числа типа BigDecimal и тому подобных. О них речь будет позже, пока что освоим обычные числа и заставим их вычисляться хотя бы приблизительно правильно. Другой вариант той же ошибки: int x = 1; int y = 3; double z = 15000000 * x / y; // сначала вычисляется выражение с его собственным типом // а сейчас в выражении целое x делится на целое y и получается машинный ноль. // то что слева стоит double не поможет - уже вычисленное выражение равно нулю. Здесь мы объявили две целочисленные переменные x и y. Снова при их делении оператор / будет воспринят как «делить нацело» и результат (int на int) будет нулевым. Так что лучше просто сделайте числа и переменные, которые участвуют в дробных выражениях типом double заранее: double x = 1; double y = 3; double z = 15000000 * x / y; // деление x на y будет одна треть, потому что x и y - дробные. Операции с типом int выполняются быстрее, чем с double, но в небольших приложениях этот выигрыш очень мал. Если всё-таки оптимизируете программу по скорости и используете int, то прошу, будьте с ним очень аккуратны! http://progtoday.ru/java-1-1-5 |