Ціле ділення: як ви робите дубль?


249

Для цього блоку коду:

int num = 5;
int denom = 7;
double d = num / denom;

значення d є 0.0. Це може бути змушене працювати шляхом кастингу:

double d = ((double) num) / denom;

Але чи є інший спосіб отримати правильний doubleрезультат? Мені не подобається кидати примітивів, хто знає, що може статися.



9
кидати "int" в подвійний - це безпечно, ви завжди отримаєте однакове значення без втрати точності.
Пітер Лорі

1
Мені хотілося б знати, чи є наступними правильними кроками, які вжив компілятор для поділу: 1) літній номер до плаваючого 2) Литий деном плавати, а також 2) ділити число на купюру. Будь ласка, повідомте мене, якщо я невірний.
mannyee

Відповіді:


156
double num = 5;

Це дозволяє уникнути акторської ролі. Але ви побачите, що конверсії в ролях є чітко визначеними. Вам не доведеться здогадуватися, просто перевірте JLS . int to double - це конверсія, що розширюється. З §5.1.2 :

Розширення примітивних перетворень не втрачає інформації про загальну величину числового значення.

[...]

Перетворення int або великого значення в плаваюче, або довгого значення в подвоєне, може призвести до втрати точності, тобто результат може втратити деякі найменш значущі біти значення. У цьому випадку отримане значення з плаваючою комою буде правильно закругленою версією цілого числа, використовуючи режим IEEE 754 «круглий-найближчий» (§4.2.4) .

5 можна виразити точно як подвійне.


60
+1. Перестаньте боятися кастингу. Дізнайтеся, як це працює. Це все чітко визначено.
Марк Петерс

1
@FabricioPH Це працює у будь-якій ситуації та ідентично до рішення * 1.0.
Вітрувій

3
@Saposhiente Це виходить за рамки роботи чи ні. Зміна типу змінної здається мені брудним кодом. Якщо у вас є щось, що ОБОВ'ЯЗКОВО бути цілим числом, і ви змінюєте подання на поплавок просто для того, щоб мати можливість виконувати математичну операцію, ви можете ризикувати собою. Також у деякому контексті читання змінної як цілого числа полегшує розуміння коду.
Fabricio PH

2
@FabricioPH, помножуючи на 1.0все ще змінює тип результату, просто по-іншому. "Здається, мені брудний код" не пояснює, за яким сценарієм, на який ви вважаєте, множення на 1.0насправді краще (або чому це може бути).
Метью Флашен

4
@FabricioPH Ви та ОП, здається, працюєте за хибною вірою (де ви говорите "Зміна типу змінної"), що виконання (double)numякимось чином змінюється num. Це не так - це створює тимчасовий подвійний для контексту висловлювання.
Пол Томблін

100

Що не так з литтям примітивів?

Якщо ви не хочете зняти участь з якихось причин, ви можете зробити це

double d = num * 1.0 / denom;

63
... що робить неявний кидок перед множенням
Еліс Перселл

2
У більшості випадків це краще, ніж зміна типу іншої змінної.
Fabricio PH

3
@FabricioPH Ніщо не говорить про те, що це правда, якщо ви не маєте джерела для цитування.
Вітрувій

1
@Saposhiente відповів у вашому іншому подібному коментарі
Fabricio PH

1
Ви також можете використовувати постфікс "D" (щоб виконати той самий неявний виступ); - так наприклад:double d = num * 1D / denom;
BrainSlugs83

73

Мені не подобається кидати примітивів, хто знає, що може статися.

Чому у вас ірраціональний страх вигнати примітивів? Нічого поганого не відбудеться, коли ви подасте intа double. Якщо ви просто не впевнені, як це працює, знайдіть його в специфікації мови Java . Приведення intTo doubleє розширенням примітивних перетворень .

Ви можете позбутися від зайвої пари дужок, вставши знаменник замість чисельника:

double d = num / (double) denom;

2
Ви також можете зробити double d = (double) num / denom;... (Гаразд, це залежить від пріоритету)
користувач85421

27

Якщо ви зміните тип однієї змінної, вам потрібно запам’ятати, щоб знову прокрастися в подвійному, якщо ваша формула зміниться, тому що якщо ця змінна перестає бути частиною обчислення, результат зіпсується. У мене є звичка робити кастинг в межах розрахунку і додавати коментар поруч.

double d = 5 / (double) 20; //cast to double, to do floating point calculations

Зауважте, що передавання результату цього не зробить

double d = (double)(5 / 20); //produces 0.0

17

Закиньте одне з цілих чисел / обидва цілого числа на плаваючий, щоб змусити операцію виконувати з плаваючою точкою Math. Інакше цілий Math завжди є кращим. Так:

1. double d = (double)5 / 20;
2. double v = (double)5 / (double) 20;
3. double v = 5 / (double) 20;

Зауважте, що передавання результату цього не зробить. Тому що перший поділ проводиться за правилом пріоритету.

double d = (double)(5 / 20); //produces 0.0

Я не думаю, що з кастингом як таким, про який ти думаєш, немає жодної проблеми.


10

Кастинг типів - єдиний спосіб


Виробляючи doubleз цілого поділу - іншого способу без кастингу немає (можливо, ви цього не зробите прямо, але це станеться).

Тепер, є кілька способів , якими ми можемо спробувати отримати точне doubleзначення (де numі denomє intтипом, і, звичайно , з литтям ) -

  1. з явним кастингом:

    • double d = (double) num / denom;
    • double d = ((double) num) / denom;
    • double d = num / (double) denom;
    • double d = (double) num / (double) denom;

але ні double d = (double) (num / denom);

  1. з неявним кастингом:

    • double d = num * 1.0 / denom;
    • double d = num / 1d / denom;
    • double d = ( num + 0.0 ) / denom;
    • double d = num; d /= denom;

але ні double d = num / denom * 1.0;
і ніdouble d = 0.0 + ( num / denom );


2

використовувати щось на кшталт:

double step = 1d / 5;

(1d - це амплуа вдвічі)


5
1d не є актором, це лише декларація.
Sefier Tang

0

Ви можете розглянути можливість завершення операцій. Наприклад:

class Utils
{
    public static double divide(int num, int denom) {
        return ((double) num) / denom;
    }
}

Це дозволяє вам шукати (лише один раз), чи виконується акторський склад саме того, що ви хочете. Цей метод також може бути випробуваний, щоб переконатися, що він продовжує робити те, що ви хочете. Також не має значення, який трюк ви використовуєте, щоб викликати поділ (ви можете використовувати будь-яку відповідь тут), якщо це призведе до правильного результату. Де б вам не було ділити два цілих числа, тепер ви можете просто зателефонувати Utils::divideі довіритись, що це робить правильно.



Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.