Интеллектуальное деление: почему результат 1/3 == 0?

107

Я писал этот код:

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

Результат - 0. Почему и как решить эту проблему?

Тофик
источник

Ответы:

147

Два операнда (1 и 3) являются целыми числами, поэтому используется целочисленная арифметика (здесь деление). Объявление переменной результата как double просто вызывает неявное преобразование после деления .

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

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

Нолдорин
источник
33
+1 и всегда округляется в меньшую сторону. int i = .99999999устанавливает int в 0. Точнее говоря, он берет целую часть и отбрасывает остальное.
Байрон Уитлок,
37
Он округляет «в сторону нуля», что означает «вниз» для значений больше нуля. (+0.9 округляется до 0, -0.9 также округляется до 0.)
Адриан Смит
@ Байрон: Да, именно так. Я не верю, что процессор на самом деле выполняет какое-либо округление, так как деление реализовано совсем по-другому, но удобно думать об этом так.
Noldorin
16
Нет, без округления: Усечение
Бэзил Бурк
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 для целочисленных операций :

Если у целочисленного оператора, отличного от оператора сдвига, есть хотя бы один операнд типа long, тогда операция выполняется с 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

Это потребует отливки с возможной потерей точности.

То же верно и для операторов с плавающей запятой.

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

Таким образом, продвижение происходит на поплавке в два раза.

И сочетание целого и плавающего значения приводит к плавающим значениям, как сказано

Если хотя бы один из операндов бинарного оператора относится к типу с плавающей запятой, тогда операция является операцией с плавающей запятой, даже если другой является целым.

Это верно для бинарных операторов, но не для «операторов присваивания», таких как +=

Достаточно простого рабочего примера, чтобы доказать это

int i = 1;
i += 1.5f;

Причина в том, что здесь выполняется неявное приведение, это будет выполняться как

i = (int) i + 1.5f
i = (int) 2.5f
i = 2
AxelH
источник
1

1 и 3 являются целочисленными константами, поэтому Java выполняет целочисленное деление, результатом которого является 0. Если вы хотите написать двойные константы, вы должны написать 1.0и 3.0.

перезагрузка
источник
1

Самое простое решение - просто сделать это

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

Что это значит, поскольку вы не вводили 1.0 / 3.0, это позволяет вам вручную преобразовать его в тип данных double, поскольку Java предполагала, что это целочисленное деление, и будет делать это, даже если это означает сужение преобразования. Это то, что называется оператором приведения.

Рамзи Эль-Джабали
источник
1

Я сделал это.

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

Используйте .0 при выполнении двойных вычислений, иначе Java будет предполагать, что вы используете целые числа. Если в вычислении используется любое количество двойных значений, то на выходе будет двойное значение. Если все целые числа, то на выходе будет целое число.

BendedWills
источник
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

Сделайте «двойной г = 1.0 / 3.0;» вместо.

Брайан Кноблаух
источник
Просто любопытно ... что не так с этим ответом? Я использовал этот подход, когда впервые столкнулся с проблемой. Было бы желательно объяснить, что не так с этим решением. Заранее спасибо.
Мистер Порт-Сент-Джо
@ Mr.PortStJoe Он не предоставляет никаких подробностей человеку, задающему вопрос. Глядя на самый популярный ответ, мы видим, что они также объяснили, ПОЧЕМУ это происходит. Хотя ответ может быть технически правильным, его полезность может быть ограничена.
Эндрю Т. Финнелл
-1

Многие другие не смогли указать на настоящую проблему:

Операция только с целыми числами переводит результат операции в целое число.

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

Что такое приведение типов / преобразование типов, спросите вы?

Это зависит от реализации языка, но Википедия имеет довольно исчерпывающий обзор, и в ней также говорится о принуждении , что является ключевой информацией при ответе на ваш вопрос.

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

сова
источник
Это ошибочный ответ. Нет никакого преобразования типов или преобразования типов, задействованных во всех целочисленных устройствах, например 1/2, в целевом языке (java). Вы просто вызываете целочисленное деление, которое приводит к целочисленному результату. Приведение типа происходит только из-за повышающего преобразования из intв doubleво время присвоения.
YoYo
@YoYo Вы говорите, что 0,5 - это целое число? Я так не думаю, братан.
sova
1
этот друг ничего не сказал 0.5. Проще говоря, в Java 1/2это целочисленное деление, которое дает нулевое целое число. Вы можете присвоить дублю ноль, он все равно будет нулем, хотя и 0.0двойным.
YoYo