Закруглення Java до int за допомогою Math.ceil


101
int total = (int) Math.ceil(157/32);

Чому вона все ще повертається 4? 157/32 = 4.90625, Мені потрібно округлитись, я озирнувся, і це, здається, є правильним методом.

Я намагався totalяк doubleтип, але отримую 4.0.

Що я роблю неправильно?

Відповіді:


187

Ви робите 157/32це ділення двох цілих чисел між собою, що завжди призводить до округлення цілого числа. Тому (int) Math.ceil(...)нічого не роблять. Є три можливі рішення для досягнення того, що ви хочете. Я рекомендую використовувати або варіант 1, або варіант 2 . Будь ласка, НЕ використовуйте варіант 0 .

## Варіант 0

Перетворіть aі bв подвійний, і ви можете використовувати поділ і так, Math.ceilяк хотіли, щоб він працював. Однак я категорично не рекомендую використовувати цей підхід, оскільки подвійний поділ може бути неточним. Детальніше про неточність парних пар див. Це питання .

int n = (int) Math.ceil((double) a / b));

## Варіант 1

int n = a / b + ((a % b == 0) ? 0 : 1); 

Ви a / bзавжди маєте підлогу, якщо aі bобидва цілі числа. Тоді ви маєте вбудовану відьму if-statement, яка перевіряє, чи слід ви перекривати замість підлоги. Тож +1 або +0, якщо є залишок із поділом, вам потрібен +1. a % b == 0чеки на залишок.

## Варіант 2

Цей варіант дуже короткий, але, можливо, для деяких менш інтуїтивно зрозумілих. Я думаю, що цей менш інтуїтивний підхід був би швидшим, ніж подвійний поділ та підхід порівняння:
Зверніть увагу, що це не працює b < 0.

int n = (a + b - 1) / b;

Щоб зменшити ймовірність переповнення, ви можете скористатися наступним. Однак врахуйте, що це не працює для a = 0та b < 1.

int n = (a - 1) / b + 1;

## Пояснення "менш інтуїтивного підходу"

Оскільки розділення двох цілих чисел у Java (та більшості інших мов програмування) завжди буде результатом. Так:

int a, b;
int result = a/b (is the same as floor(a/b) )

Але ми не хочемо floor(a/b), але ceil(a/b), використовуючи визначення та графіки з Вікіпедії :введіть тут опис зображення

За допомогою цих ділянок функції підлоги та стелі ви можете побачити взаємозв'язок.

Функція підлоги Функція стелі

Ви можете це бачити floor(x) <= ceil(x). Нам потрібно floor(x + s) = ceil(x). Тому нам потрібно знайти s. Якщо ми вважаємо, що 1/2 <= s < 1це буде правильно (спробуйте кілька цифр, і ви побачите, що це є, мені важко самому це довести). І 1/2 <= (b-1) / b < 1так

ceil(a/b) = floor(a/b + s)
          = floor(a/b + (b-1)/b)
          = floor( (a+b-1)/b) )

Це не справжнє підтвердження, але сподіваюся, що Ви цим задоволені. Якщо хтось може це краще пояснити, я також вдячний. Можливо, запитайте це на MathOverflow .


1
Буде величезна користь, якщо ви зможете пояснити інтуїцію, що стоїть за менш інтуїтивним підходом? Я знаю, що це правильно, я хочу знати, як ви потрапили до цього, і як я можу математично показати, що це правильно. Я намагався вирішити це математично, не був переконаний.
Саад Рехман Шах

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

Я припускаю, що Math.floor і ceil правильні лише для цілого поділу, а не для тривалого поділу, коли значення передаються в подвійні. Приклади лічильників 4611686018427386880/4611686018427387137 провалюються на підлозі і 4611686018427386881/4611686018427386880 провалюються на перекриття
Wouter

2
Пояснення: результати двох варіантів варіанту 2 не є однаковими у всіх випадках. Значення нуля для атрибута дасть 0 у першому та 1 у другому (що не є правильною відповіддю для більшості програм).
Sushisource

1
Ви впевнені, що не мали на увазі "Однак зауважте, що він не працює для a = 0 та b < 1"
dantiston

60

157/32 є int/int, що призводить до int.

Спробуйте скористатися подвійним літералом - 157/32d, що є int/doubleрезультатом а double.


Ви впевнені, що int / int завжди призведе до int ?! ви можете, будь ласка, дати джерело для цього ?!


34

157/32це ціле ділення, тому що всі числові букви є цілими числами, якщо інше не вказано суфіксом ( dдля подвійного lна довгий)

поділ округляється вниз (до 4), перш ніж він перетворюється в подвійний (4.0), який потім округляється вгору (до 4.0)

якщо ви використовуєте змінні, ви можете цього уникнути

double a1=157;
double a2=32;
int total = (int) Math.ceil(a1/a2);


7

Ніхто не згадав про найінтуїтивніше:

int x = (int) Math.round(Math.ceil((double) 157 / 32));

Це рішення фіксує точність подвійного поділу.


1
Math.round повертається довго
Zulqurnain Jutt

Спасибі @ZulqurnainJutt, додав акторський склад
IG Pascual


3

При поділі двох цілих чисел, наприклад,

int c = (int) a / (int) b;

результат - an int, значення якого aділиться на b, округлене до нуля. Оскільки результат уже округлий, ceil()нічого не робить. Зауважте, що це округлення не те саме floor(), що округлює до негативної нескінченності. Отже, 3/2дорівнює 1floor(1.5)дорівнює 1.0, але (-3)/2дорівнює -1(але floor(-1.5)дорівнює -2.0).

Це дуже важливо , тому що якщо a/bзавжди були такими ж , як floor(a / (double) b), то можна просто реалізувати ceil()в a/bякості -( (-a) / b).

Пропозиція отримати ceil(a/b)від

int n = (a + b - 1) / b;, що еквівалентно a / b + (b - 1) / b, або(a - 1) / b + 1

працює, тому що ceil(a/b)завжди на один більший floor(a/b), за винятком випадків, коли a/bце ціле число. Отже, ви хочете зіткнути його на (або минуле) наступне ціле число, якщо тільки a/bце не ціле число. Додавання 1 - 1 / bзробить це. Для цілих чисел це не дуже підштовхне їх до наступного цілого числа. На все інше воно буде.

Yikes. Сподіваємось, це має сенс. Я впевнений, що є більш математично вишуканий спосіб пояснити це.


2

Для перетворення числа з цілого числа в реальне число ви можете додати крапку:

int total = (int) Math.ceil(157/32.);

І результат (157/32.) Теж буде реальним. ;)



1

Ознайомтесь із рішенням нижче, яке має своє запитання:

int total = (int) Math.ceil(157/32);

Тут слід помножити Numerator на 1.0, тоді він дасть вашу відповідь.

int total = (int) Math.ceil(157*1.0/32);

0

Використовуйте подвійний, щоб відіграти як

Math.ceil((double)value) чи як

Math.ceil((double)value1/(double)value2);

0

Java /за замовчуванням надає лише ділення підлоги . Але ми можемо написати стелю з точки зору підлоги . Подивимось:

Будь-яке ціле число yможна записати за допомогою форми y == q*k+r. Відповідно до визначення поділу підлоги (тут floor), який завершується r,

floor(q*k+r, k) == q  , where 0  r  k-1

і поділу стелі (тут ceil), який округляється r₁,

ceil(q*k+r₁, k) == q+1  , where 1  r  k

де ми можемо замінити r+1на r₁:

ceil(q*k+r+1, k) == q+1  , where 0  r  k-1


Тоді ми замінюємо перше рівняння на третє для qотримання

ceil(q*k+r+1, k) == floor(q*k+r, k) + 1  , where 0  r  k-1

Нарешті, з огляду на будь-яке ціле число , yде y = q*k+r+1для деяких q, k, rми маємо

ceil(y, k) == floor(y-1, k) + 1

І ми зробили. Сподіваюся, це допомагає.


Я впевнений, що це правильно, але оскільки суть цього полягає в з'ясуванні, мені незрозуміло, чому ceilвизначається як таке з інтитуативного визначення, зокрема, де ми беремо стель цілого числа, тобто r1 = k. Оскільки крайові випадки є складними в цьому, я думаю, що це потрібно прописати трохи більше.
Луїджі Плінг

@LuigiPlinge Для мене виведення не може бути простішим через внутрішню різницю між підлогою та стелею в контексті роботи поділу. Я думаю, що вам не потрібно зосереджуватися на крайовому корпусі - це природний факт, коли ви намагаєтесь уніфікувати визначення підлоги та стелі шляхом розбиття цілого числа. В результаті доказ - це лише три кроки, і висновок можна приблизно згадати як "один амортизований крок назад, потім один абсолютний крок вперед".
ShellayLee

0

Є два способи, за допомогою яких можна округлити подвійне значення.

  1. Math.ceil
  2. Math.поверх

Якщо ви хочете, щоб ваша відповідь 4.90625 була 4, тоді ви повинні використовувати Math.floor, а якщо ви хочете, щоб відповідь 4.90625 була 5, тоді ви можете використовувати Math.ceil

Для цього можна посилатись наступним кодом.

public class TestClass {

    public static void main(String[] args) {
        int floorValue = (int) Math.floor((double)157 / 32);
        int ceilValue = (int) Math.ceil((double)157 / 32);
        System.out.println("Floor: "+floorValue);
        System.out.println("Ceil: "+ceilValue);

    }

}

-3
int total = (157-1)/32 + 1

або більш загальне

(a-1)/b +1 

Я думаю, що це працює, але ви не дуже пояснили, чому оригінальна версія не працювала.
Teepeemm

" Однак зауважте, що він не працює для a = 0 і b <1 "
IG Pascual
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.