JavaRush /Java блог /Random UA /BigInteger та BigDecimal

BigInteger та BigDecimal

Стаття з групи Random UA
У мові Java додаток складається з класів, а класи - з методів та змінних. Змінні, своєю чергою, діляться на примітивні та посилальні. BigInteger та BigDecimal - 1У Java є 8 видів змінних і, якщо не рахувати booleanі char, вони поділяються на такі типи:
  • цілі: byte, short, intі long;
  • з плаваючою точкою (також називають речовими числами): floatі double​​.
Усередині цих невеликих груп відмінності існують тільки в діапазоні значень, що вміщаються (і відповідно, різниться займане місце такої змінної). Найбільший з цілих типів — long, з діапазоном від -9223372036854775808 до 9223372036854775807. З чисел з плаваючою точкою — double, з діапазоном від 1.7e-308 до 1.7e+308. Докладніше про речові числа можна почитати в цій статті . Але якщо нам знадобиться зберігати числа, які перевищують допустимий діапазон? У такому разі нам знадобляться BigIntegerі BigDecimal.

BigInteger в Java

Клас Java BigIntegerвикористовується як аналог цілісних значень довільної довжини, який не має обмеження в 64 бітів, як наприклад у long. При цьому він нащадок класу Number, як і стандартні обгортки для числових простих типів - Integer, Long, Byte, Doubleі так далі - тому має реалізації методів, що призводять до простих типів:
BigInteger value = new BigInteger("32145");

int intValue = value.intValue();//32145

long longValue = value.longValue();//32145

double doubleValue = value.doubleValue();//32145.0
Тут ми бачимо створення такого об'єкта BigIntegerз передачею в конструктор нашого значення, але у форматі рядка. Конструктор у нього не один, а на всі випадки життя. Якщо прості типи не містять повний обсяг даних з BigInteger, дані будуть урізані до діапазону цього примітивного типу. Але при цьому є аналоги цих методів ( intValueExact(), longValueExact()і т. д.), з тією лише різницею, що якщо простий тип, в який йде перетворення, не містить діапазон даних, викидається ArithmeticException .

Константи BigInteger

Для внутрішнього користування клас має константи:
BigInteger.ZERO
BigInteger.ONE
BigInteger.TEN
Це константні об'єкти BigIntegerзі значеннями, відповідно, 0, 1і 10.

Методи BigInteger

Однією з головних особливостей цього класу є повний методів, що реалізують стандартні арифметичні операції на Java. Наприклад:
  • операції підсумовування:

    BigInteger firstValue = new BigInteger("37995");
    BigInteger secondValue = new BigInteger("35466");
    BigInteger resultValue =  firstValue.add(secondValue);//73461
  • операції множення:

    BigInteger firstValue = new BigInteger("37995");
    BigInteger secondValue = new BigInteger("35466");
    BigInteger resultValue =  firstValue.multiply(secondValue);//1347530670
  • операції знаходження залишку при розподілі одного числа на інше:

    BigInteger firstValue = new BigInteger("37995");
    BigInteger secondValue = new BigInteger("35466");
    BigInteger resultValue =  firstValue.remainder(secondValue);//2529
  • отримання абсолютного значення числа (тобто за модулем, без знака):

    BigInteger firstValue = new BigInteger("-37995");
    BigInteger resultValue =  firstValue.abs();//37995
Також є методи для більш складних (специфічних) операцій:
  • операції з обчисленням mod :

    BigInteger firstValue = new BigInteger("-34");
    BigInteger secondValue = new BigInteger("5");
    BigInteger resultValue = firstValue.mod(secondValue); //1
Існують кілька різних варіацій цієї функції:
  • отримання рандомного числа із завданням кількості бітів, яке використовуватиме отримане значення:

    BigInteger firstValue = BigInteger.probablePrime(8, new Random());//211
    BigInteger secondValue = BigInteger.probablePrime(16, new Random());//42571
  • операції побітових зрушень(this >> n)

    Зрушення вліво:

    BigInteger firstValue = new BigInteger("5");
    BigInteger firstResultValue = firstValue.shiftLeft(3);//40

    Зсув праворуч:

    BigInteger secondValue = new BigInteger("34");
    BigInteger secondResultValue = secondValue.shiftRight(2); //8
Звичайно ж, повний перелік методів краще подивитися у документації . BigInteger та BigDecimal - 2

BigDecimal у Java

Коли нам потрібне дійсне число довільної довжини, використовується клас Java - BigDecimal. Як правило його застосовують для роботи з фінансами замість doubleтого, що він дає більше можливості налаштування. Як і BigIntegerє BigDecimalнащадком класу Numberі має методи, що повертають значення об'єкта у вигляді певного примітивного типу:
BigDecimal value = new BigDecimal(35563.3);

long longValue = value.longValue();//35563

double doubleValue = value.doubleValue();//35563.3
Як ми можемо бачити при наведенні до long, залишається лише ціла частина, а знаки після коми відкидаються.

Конструктори BigDecimal

З конструкторами BigDecimalми ознайомимося докладніше, оскільки клас має їх набагато ширший вибір. Є конструктори, дозволяють задати значення об'єкта різними способами (передачею int, long, double, Stringі навіть BigInteger), а є такі, які дозволяю. задавати налаштування створюваного об'єкта (способи округлення, кількість знаків після коми):
BigDecimal firstValue = new BigDecimal("455656.545");//455656.545
Тут все зрозуміло, ми безпосередньо задали значення та кількість знаків після коми, які хочемо бачити.
BigDecimal secondValue = new BigDecimal(3445.54);//3445.5399999999999636202119290828704833984375
Результати цього конструктора можуть бути непередбачуваними, адже ми задаємо double, який за своєю природою дуже неоднозначний тип. Тому зазвичай рекомендується використовувати у конструкторі String.
BigDecimal thirdValue = new BigDecimal(3445.554645675444, MathContext.DECIMAL32);//3445.555
Ми задаємо double, але при цьому і задаємо параметр, який описує правило округлення (який містить кількість знаків після коми та алгоритм для округлення).
char[] arr = new String("455656.545").toCharArray();

BigDecimal fourthValue = new BigDecimal(arr, 2, 6);//5656.5
Задаємо масив знаків, з якого елемента беремо значення об'єкта і скільки цих елементів беремо.
BigDecimal fifthValue = new BigDecimal(new BigInteger("44554"), 3);//44.554
Беремо вже існуючий об'єкт BigInteger, задаємо якусь кількість знаків після коми.

Методи BigDecimal

Клас BigDecimalтакож містить у собі методи для різноманітних арифметичних операцій, але методів роботи з бітами, як у BigInteger, у нього немає. Проте головна фішка BigDecimal— гнучкість у роботі з числами з плаваючою комою. Давайте розглянемо деякі методи, які дають нам можливість панувати над речовими числами:
  • отримуємо точність (кількість чисел):

    BigDecimal value = new BigDecimal("454334.34334");
    int result = value.precision();//11
  • задаємо кількість знаків після коми та правило округлення:

    BigDecimal firstValue = new BigDecimal(3445.544445);
    
    BigDecimal secondValue = firstValue.setScale(3,BigDecimal.ROUND_CEILING);//3445.545

    Дещо нижче ми розглянемо докладніше константи для завдання правил округлення.

  • ділимо BigDecimalна інший BigDecimal, при цьому вказуючи необхідну кількість знаків після коми і правило округлення:

    BigDecimal firstValue = new BigDecimal("455656.545");
    
    BigDecimal secondValue = new BigDecimal(3445.544445);
    
    BigDecimal result = firstValue.divide(secondValue, 2,RoundingMode.DOWN);//132.24
  • переміщення комою вправо/ліворуч на певну кількість знаків:

    BigDecimal value = new BigDecimal("455656.545");
    BigDecimal firstResult = value.movePointRight (2);//45565654.5
    BigDecimal secondResult = value.movePointLeft (2);//4556.56545
  • обрізаємо кінцеві нулі:

    BigDecimal value = new BigDecimal("45056.5000");
    BigDecimal result = value.stripTrailingZeros();//45056.5

    Якщо ж у нас у речовій частині всі нулі і в цілій теж є (або у нас зовсім немає знаків після коми), тоді:

    BigDecimal value = new BigDecimal("450000.000");
    BigDecimal result = value.stripTrailingZeros();//4.5E+5

Правила округлення BigDecimal

Для завдання правил округлення, всередині BigDecimalми можемо побачити спеціальні константи, що описують алгоритми округлення: ROUND_UP- Заокруглення від нуля, округлення в бік речовинної частини:
BigDecimal firstValue = new BigDecimal("2.578");
BigDecimal firstResult =  firstValue.setScale(1, BigDecimal.ROUND_UP );//2.6
BigDecimal secondValue = new BigDecimal("-2.578");
BigDecimal secondResult = secondValue.setScale(1, BigDecimal.ROUND_UP );//-2.5
ROUND_DOWN- Округлення до нуля, тобто усічення речовинної частини:
BigDecimal firstValue = new BigDecimal("2.578");
BigDecimal firstResult =  firstValue.setScale(1, BigDecimal.ROUND_DOWN  );//2.5
BigDecimal secondValue = new BigDecimal("-2.578");
BigDecimal secondResult = secondValue.setScale(1, BigDecimal.ROUND_DOWN  );//-2.6
ROUND_CEILING- Округлення до позитивної нескінченності. Тобто якщо число у нас позитивне, то -> ROUND_UP, якщо негативне, то ->ROUND_DOWN
BigDecimal firstValue = new BigDecimal("2.578");
BigDecimal firstResult =  firstValue.setScale(1, BigDecimal.ROUND_CEILING);//2.6
BigDecimal secondValue = new BigDecimal("-2.578");
BigDecimal secondResult = secondValue.setScale(1, BigDecimal.ROUND_CEILING);//-2.5
ROUND_FLOOR- Округлення до негативної нескінченності, тобто якщо число у нас позитивне, то -> ROUND_DOWN, якщо негативне, то ->ROUND_UP
BigDecimal firstValue = new BigDecimal("2.578");
BigDecimal firstResult =  firstValue.setScale(1, BigDecimal.ROUND_FLOOR);//2.5
BigDecimal secondValue = new BigDecimal("-2.578");
BigDecimal secondResult = secondValue.setScale(1, BigDecimal.ROUND_FLOOR);//-2.6
Для значення, що розглядається, це найближче число з урізаним знаком після коми будемо розглядати як найближчого сусіда розглянутого числа. Наприклад, 2.43 буде ближче до 2.4, ніж до 2.5, але 2.48 буде ближче до 2.5. ROUND_HALF_DOWN- Округлення до «найближчого сусіда». Якщо обидва сусіди рівновіддалені від конкретного значення, проводиться округлення до нуля. Рівновіддалені - це, наприклад, коли округлюване число - 5, і воно знаходиться на однаковій відстані від 0 і 10):
BigDecimal firstValue = new BigDecimal("2.58");
BigDecimal firstResult =  firstValue.setScale(1, BigDecimal.ROUND_HALF_DOWN );//2.6
BigDecimal secondValue = new BigDecimal("2.55");
BigDecimal secondResult = secondValue.setScale(1, BigDecimal.ROUND_HALF_DOWN );//2.5
ROUND_HALF_UP— режим округлення у бік «найближчого сусіда». Якщо обидва сусіди рівновіддалені, округлення виконується до більшого числа (це саме округлення, якому нас навчали в школі):
BigDecimal firstValue = new BigDecimal("2.53");
BigDecimal firstResult =  firstValue.setScale(1, BigDecimal.ROUND_HALF_UP  );//2.5
BigDecimal secondValue = new BigDecimal("2.55");
BigDecimal secondResult = secondValue.setScale(1, BigDecimal.ROUND_HALF_UP  );//2.6
ROUND_HALF_EVEN— округлення до «найближчого сусіда», якщо обидва сусіди не віддалені. У разі якщо перед округленим числом стоїть непарне, виконується округлення у більшу сторону, якщо парне — меншу:
BigDecimal firstValue = new BigDecimal("2222.2225");
BigDecimal secondValue = firstValue.setScale(3,BigDecimal.ROUND_HALF_EVEN );//2222.222
Такий результат ми отримуємо, тому що при округленні 5 дивиться на попереднє число 2, і бачачи, що воно парне округлення йде меншу сторону. Але якщо:
BigDecimal firstValue = new BigDecimal("2222.22255");
BigDecimal secondValue = firstValue.setScale(3,BigDecimal.ROUND_HALF_EVEN );//2222.223
Те округлення йде у бік, оскільки остання 5 дивиться на попереднє значення і бачить непарне число. Як наслідок, число округляється у велику сторону до 6, після чого наступна 6 теж йде на заокруглення. Але шістка вже не дивиться на число зліва, оскільки число явно ближче у велику сторону, і в результаті остання 2 збільшується на 1. ROUND_UNNECESSARY- використовується для перевірки, що число округлення не потребує. Тобто, ми перевіряємо, що число має потрібну нам кількість знаків після коми:
BigDecimal firstValue = new BigDecimal("2.55");
BigDecimal firstResult =  firstValue.setScale(2, BigDecimal.ROUND_UNNECESSARY);//2.55
Тут все добре, значення має два знаки і ми перевіряємо, що після коми лише два знаки. Але якщо:
BigDecimal secondValue = new BigDecimal("2.55");
BigDecimal secondResult = secondValue.setScale(1, BigDecimal.ROUND_UNNECESSARY);
Ми отримаємо — ArithmeticException, оскільки перевірене значення перевищує задану кількість знаків після коми. Але якщо ми перевірятимемо два знаки після коми, а за фактом там є один, то виняток не випаде, а знаки, що бракують, просто доповнюються нулями:
BigDecimal thirdValue = new BigDecimal("2.5");
BigDecimal thirdResult = thirdValue.setScale(3, BigDecimal.ROUND_UNNECESSARY   );//2.500
Ще хотілося б відзначити що BigDecimalє константи, аналогічні константам BigInteger ZERO, ONEі TEN. Ось посилання на документацію . І насамкінець: як ви, напевно, помітабо, виконуючи операції з об'єктами BigIntegerі BigDecimalми не змінюємо старі, а завжди отримуємо нові. Це нам говорить про те, що вони — immutableтобто незмінні після їх створення, як і String. Інакше кажучи, всі їх методи що неспроможні змінити внутрішній стан об'єкта, максимум — повернути новий об'єкт із параметрами, заданими використовуваної нами функцією.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ