Главная страница
Навигация по странице:

  • 15e3

  • Приводим один тип к другому

  • double

  • Деление нацело (int на int) - популярный источник ошибок Когда мы используем оператор /

  • int

  • Деление нацело типы. Типы данных в Java Приведение типов Коварное деление нацело. Int, double, char


    Скачать 26.84 Kb.
    НазваниеInt, double, char
    АнкорДеление нацело типы
    Дата01.09.2022
    Размер26.84 Kb.
    Формат файлаdocx
    Имя файлаТипы данных в Java Приведение типов Коварное деление нацело.docx
    ТипДокументы
    #657923

    Типы данных в Java
    Приведение типов
    Коварное деление нацело


    Мы уже знаем несколько типов данных - intdoublechar. Есть еще и другие типы, созданные под конкретные цели. Выражение одного типа можно "приводить" к выражению другого типа. Например, приводя дробные к целым, мы отбрасываем дробную часть. Java также не учитывает дробную часть, когда делит одно целое число на другое. Если важен дробный результат, то следует пользоваться дробным типом.

    В следующей таблице приведены все основные типы данных Java. Указано, сколько бит выделяется под переменную каждого из типов. Биты – это цифры двоичной системы исчисления. В привычной человеку системе десять цифр, а в двоичной – только две, это 1 и 0. Все числа в машине переводятся в двоичную систему. Если в типе данных 8 бит, то на это число отводится 8 двоичных цифр. Вы не можете записать число из девяти цифр – девятая цифра не поместится в ячейку и реально в числе сохранится не настоящий результат, а совершенно ложный - без одной из цифр. Поэтому у каждого типа данных есть максимальное и минимальное допустимое значение. Они доступны во всех справочниках, я привожу их здесь для общего понимания.

    Сначала приведем таблицу типов данных для целых чисел - byteshortcharintlong.

    Если вы уверены, что вам нужны совсем небольшие числа, то можете сэкономить оперативную память и использовать byte. Если же напротив, вы делаете расчет с числами больше двух миллиардов, то вам нужен long. Поначалу вполне можно ограничиться средним вариантом - привычным int.

    Тип

    Размер (бит)

    Диапазон

    byte

    8 бит

    от -128 до 127

    short

    16 бит

    от -32768 до 32767

    char

    16 бит

    Без знаковое целое число, представляющее собой символ UTF-16 (буквы и цифры)

    int

    32 бит

    от -2147483648 до 2147483647

    long

    64 бит

    от -9223372036854775808 до 9223372036854775807

    Дробные типы данных – 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 тысячных). По сути порядок описывает сколько нулей нужно к числу приписать, или же где в числе поставить точку десятичной дроби. Именно в таком виде дроби хранятся в памяти компьютера.

    Тип

    Размер (бит)

    Диапазон

    float

    32

    от малого 1.4e-45f до большого 3.4e+38f, и такой же диапазон для отрицательных

    double

    64

    от 4.9e-324 до 1.7e+308

    Есть специальный тип boolean, который служит для хранения результатов логических выражений. Эти выражения мы подробно рассмотрим в следующем разделе, а пока укажу, что их результат всегда либо правда (true), либо ложь (false). То есть логическое выражение или истинно, или ложно. Поэтому в Java мы можем написать:

    boolean res = true; // переменная res хранит "правду" - true

    res = false; // теперь res хранит "ложь" - false
    int x = 10;
    res = x > 20; // 10 меньше 20, поэтому сравнение x больше 20 дает ложь

    Тип

    Размер (бит)

    Диапазон

    boolean

    обычно 32, но официально может быть любым

    true (истина) или false (ложь)

    Приводим один тип к другому

    Что если вы хотите переменную или число одного типа записать в переменную другого типа? Если точность наверняка не страдает, то Java разрешит вам это сделать с помощью присваивания. Если же точность может упасть, например, при отбрасывании дробной части, Java просит программиста удостоверить, что он действительно хочет преобразовать один тип в другой. Это делается так:

    double a = 2.2;

    // привести дробное a к целому b

    int b = (int) a;

    // скобки слева от имени переменной или от какого-либо числового выражения

    // обозначают "привести это выражение к новому типу, указанному в скобках".

    Определение. Когда перед переменной или выражением слева в скобках указывается тип, то эта переменная или выражение приводится к данному типу. Может быть понижена точность вычислений. Так программист удостоверяет, что он и в самом деле хочет совершить небезопасную операцию.

    Еще пример:

    // привести точное выражение double к менее точному float

    double a = 2.2

    float x = (float) a*10;

    Изменение типа будет осуществлено только при определённых условиях. Главное правило: если точность не теряется, то преобразование типов пройдет само собой (неявно, то есть скобки с типом можно не указывать), если же точность может пострадать, например, в присваивании long к intdouble к 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 не поможет - уже вычисленное выражение равно нулю.
    Здесь мы объявили две целочисленные переменные и 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


    написать администратору сайта