Внутрішній поділ: Чому результат 1/3 == 0?


107

Я писав цей код:

public static void main(String[] args) {
    double g = 1 / 3;
    System.out.printf("%.2f", g);
}

Результат 0. Чому це так, і як я вирішую цю проблему?

Відповіді:


147

Два операнди (1 і 3) є цілими числами, тому використовується ціла арифметика (поділ тут). Оголошення змінної результату подвійним просто викликає неявне перетворення після поділу .

Ціле ділення звичайно повертає справжній результат ділення, округлений до нуля. Таким чином, результат 0.333...округлюється до 0. (Зауважте, що процесор насправді не проводить жодного округлення, але ви можете все це думати.)

Також зауважте, що якщо обидва операнди (числа) задані як плавні; 3.0 і 1.0, або навіть лише перший , тоді використовується арифметика з плаваючою комою, що дає вам 0.333....


33
+1, і це завжди округляється вниз. int i = .99999999встановлює int до 0. Більш конкретно, він займає цілу частину і відкидає решту.
Байрон Вітлок

37
Він округляється "до нуля", що є "вниз" для значень, більших за нуль. (+0,9 округляється до 0, -0,9 також округлюється до 0.)
Адріан Сміт

@Byron: Так, точно. Я не вірю, що процесор насправді робить якесь округлення, оскільки поділ реалізується дуже по-різному, але зручно думати про це таким чином.
Нолдорін

15
Ні, не округлення: усічення
Василь Бурк

3
@BasilBourque У термінології java округленняDOWN до нуля. Округлення FLOORйде до негативної нескінченності.
Андреас

23

1/3 використовує ціле ділення, оскільки обидві сторони є цілими числами.

Ви повинні принаймні , один з них , щоб бути floatабо double.

Якщо ви вводите значення у вихідний код, як ваше запитання, ви можете це зробити 1.0/3; 1.0є подвійний.

Якщо ви отримуєте значення з інших місць, ви можете використовувати їх (double)для перетворення intв double.

int x = ...;
int y = ...;
double value = ((double) x) / y;

10

Явно виклав це як double

double g = 1.0/3.0

Це відбувається тому, що Java використовує цілочисельну операцію поділу для 1і 3оскільки ви ввели їх як цілі константи.


2

ви повинні використовувати

double g=1.0/3;

або

double g=1/3.0;

Ціле ділення повертає ціле число.


2

Тому що ви робите ціле ділення.

Як говорить @Noldorin, якщо обидва оператори є цілими числами, то використовується ціле ділення.

Результат 0,33333333 не може бути представлений як ціле число, тому результату присвоюється лише ціла частина (0).

Якщо будь-який з операторів є double/ float, то має місце арифметика з плаваючою точкою. Але у вас буде та сама проблема, якщо ви це зробите:

int n = 1.0 / 3.0;

1

Оскільки він трактує 1 і 3 як цілі числа, тому округлюючи результат до 0, так що це ціле число.

Щоб отримати результат, який ви шукаєте, чітко скажіть java, що цифри вдвічі такі:

double g = 1.0/3.0;

1

Зробіть 1 поплавцем і буде використано поділ поплавця

public static void main(String d[]){
    double g=1f/3;
    System.out.printf("%.2f",g);
}

1

Перетворення в JAVA досить просте, але потребує певного розуміння. Як пояснено в JLS для цілих операцій :

Якщо цілий оператор, окрім оператора зсуву, має принаймні один операнд типу довгий, то операція проводиться з використанням 64-бітової точності, а результат числового оператора має тип long. Якщо інший операнд не довгий, його спочатку розширюють (§5.1.5), щоб набрати довге шляхом числового просування (§5.6).

І приклад - це завжди найкращий спосіб перекладати JLS;)

int + long -> long
int(1) + long(2) + int(3) -> long(1+2) + long(3)

В іншому випадку операція проводиться з використанням 32-бітної точності, а результат числового оператора має тип int. Якщо будь-який операнд не є int, він спочатку розширюється на тип int за допомогою числового просування.

short + int -> int + int -> int

Невеликий приклад використання Eclipse, щоб показати, що навіть додавання двох shorts не буде таким простим:

short s = 1;
s = s + s; <- Compiling error

//possible loss of precision
//  required: short
//  found:    int

Для цього знадобиться лиття з можливою втратою точності.

Те саме стосується і операторів плаваючою точкою

Якщо хоча б один з операндів числовому оператору має тип подвійний, то операція проводиться за допомогою арифметики з плаваючою комою з 64 бітами, а результат числового оператора - значення типу подвійне. Якщо інший операнд не є подвійним, його спершу розширюють (§5.1.5), щоб набрати подвійне числове просування (§5.6).

Тож просування робиться на поплавці в подвійний.

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

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

Це стосується двійкових операторів, але не для "Операторів призначення" +=

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

int i = 1;
i += 1.5f;

Причина в тому, що тут робиться неявна роль, це буде виконуватися як би

i = (int) i + 1.5f
i = (int) 2.5f
i = 2

1

1 і 3 є цілими контактами, і тому Java робить цілочисельний поділ, результат якого дорівнює 0. Якщо ви хочете записати подвійні константи, потрібно записати 1.0і 3.0.


1

Найпростіше рішення просто зробити це

double g = ((double) 1 / 3);

Це робить, оскільки ви не ввели 1.0 / 3.0, - це дозволити вам вручну перетворити його в тип даних подвійний, оскільки Java припускає, що це розділення Integer, і це буде робити це, навіть якщо це означало звуження перетворення. Це те, що називається оператором лиття.


1

Я зробив це.

double g = 1.0/3.0;
System.out.printf("%gf", g);

Використовуйте .0, роблячи подвійні обчислення, інакше Java припустить, що ви використовуєте цілі. Якщо для обчислення використовується будь-яка кількість подвійних значень, то вихід буде подвійним значенням. Якщо всі цілі числа, то вихід буде цілим числом.


0

(1/3) означає поділ цілого числа, ось чому ви не можете отримати десяткове значення з цього поділу. Щоб вирішити цю проблему, використовуйте:

public static void main(String[] args) {
        double g = 1.0 / 3;
        System.out.printf("%.2f", g);
    }

0
public static void main(String[] args) {
    double g = 1 / 3;
    System.out.printf("%.2f", g);
}

Оскільки і 1, і 3 є входами, результат не округлюється, а є усіченим. Так ви ігноруєте дроби і берете лише цілі.

Щоб уникнути цього, майте принаймні одне з ваших чисел 1 або 3 у вигляді десяткової форми 1.0 та / або 3.0.


0

Спробуйте це:

public static void main(String[] args) {
    double a = 1.0;
    double b = 3.0;
    double g = a / b;
    System.out.printf(""+ g);
}

Будь ласка, не публікуйте відповідей, що стосуються лише коду, додайте пояснення, що робить ваш код, і як (і чому) він вирішує проблему. Також врахуйте, що це 8-річне запитання, на яке існують актуалізовані відповіді, і чи додає ваша відповідь значення над існуючими відповідями.
Марк Ротвевель

-1

Зробіть "подвійний g = 1,0 / 3,0;" замість цього.


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

@ Mr.PortStJoe Він не надає жодних деталей особі, яка задає питання. Дивлячись на найкраще оцінену відповідь, ми можемо побачити, що вони пояснили, ЧОМУ це теж відбувається. Хоча відповідь може бути технічно правильною, її корисність може розглядатися як обмежена.
Ендрю Т Фіннелл

-1

Багато інших не зазначили справжнього питання:

Операція з лише цілими числами кидає результат операції на ціле число.

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

Що ви запитуєте ( кастинг / перетворення типів), яке ви запитуєте?

Це залежить від мовної реалізації, але у Вікіпедії досить вичерпний погляд, і він також говорить про примус , який є ключовою інформацією при відповіді на ваше запитання.

http://en.wikipedia.org/wiki/Type_conversion


Ця відповідь є хибною. У лицьовому розрізі, наприклад 1/2, не на цільовій мові (java), не використовується кастинг типу або перетворення типів . Ви просто викликаєте ціле ділення, що призведе до цілого результату. Кастинг типів відбувається лише завдяки перетворенню вгору з intдо doubleпід час призначення.
YoYo

@YoYo Ви говорите, що 0,5 - це ціле число? Я не думаю, хамі.
сова

1
цей гомій нічого про це не говорив 0.5. Просто в Java 1/2- це ціле ділення, яке призводить до нульового цілого числа. Ви можете призначити нуль подвійному, він все одно буде нулем, хоч і 0.0подвійним.
YoYo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.