Каков правильный приоритет математического выражения

Какова правильная последовательность математических операций в этом выражении в Java:

    a + b  * c / ( d - e )
1.    4    1   3     2
2.    4    2   3     1

Я понимаю, что в обоих ответах результат одинаковый. Но я хотел бы полностью понять логику компилятора java. Что выполняется первым в этом примере - умножение или выражение в круглых скобках? Ссылка на документацию, которая охватывает это, будет полезна.

ОБНОВЛЕНИЕ: Спасибо, ребята, за ответы. Большинство из вас пишут, что выражение в круглых скобках сначала оценивается. Изучив ссылки, предоставленные Гродригесом, я создал небольшие тесты:

int i = 2;
System.out.println(i * (i=3)); // prints '6'
int j = 2;
System.out.println((j=3) * j); // prints '9'

Может ли кто-нибудь объяснить, почему эти тесты дают разные результаты? Если выражение в круглых скобках оценивается первым, я ожидаю тот же результат - 9.

Ответы

Ответ 1

Почти все до сих пор путали порядок оценки с приоритетом оператора. В Java правила приоритета делают выражение эквивалентным следующему:

a + (b  * c) / ( d - e )

поскольку * и / имеют одинаковый приоритет и остаются ассоциативными.

Порядок оценки строго определяется как левый операнд сначала, затем правый, затем операция (кроме || и && & &). Таким образом, порядок оценки:

  a
      b
      c
    *
      d
      e
    -
  /
+

порядок оценки идет по странице. Отступ отражает структуру дерева синтаксиса

Edit

В ответ на комментарии Гродригеса. Следующая программа:

public class Precedence 
{
    private static int a()
    {
        System.out.println("a");
        return 1;
    }   
    private static int b()
    {
        System.out.println("b");
        return 2;
    }
    private static int c()
    {
        System.out.println("c");
        return 3;
    }
    private static int d()
    {
        System.out.println("d");
        return 4;
    }
    private static int e()
    {
        System.out.println("e");
        return 5;
    }

    public static void main(String[] args) 
    {
        int x = a() + b() * c() / (d() - e());
        System.out.println(x);
    } 
}

Производит вывод

a
b
c
d
e
-5

который ясно показывает, что умножение выполняется до вычитания.

Ответ 2

Как показал нам JeremyP , первый ответ правильный.

В общем случае применяются следующие правила:

  • Каждый операнд оператора вычисляется до выполнения самой операции (кроме ||, && и ? :)
  • Операнды оцениваются слева направо. Левый операнд двоичного оператора оказывается полностью оцененным до того, как будет оценена любая часть правого операнда.
  • Порядок оценки соответствует скобкам и приоритету оператора:
    • Сначала оцениваются скобки.
    • Операторы оцениваются в порядке приоритета.
    • Операторы с одинаковым приоритетом оцениваются слева направо, за исключением операторов присваивания, которые оцениваются справа налево.

Обратите внимание, что первые два правила объясняют результат в вашем втором вопросе:

int i = 2;
System.out.println(i * (i=3)); // prints '6'
int j = 2;
System.out.println((j=3) * j); // prints '9'

Справочная документация:

http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#4779

Учебник:

http://download.oracle.com/javase/tutorial/java/nutsandbolts/operators.html

Ответ 3

Он вычисляет выражения в следующем порядке. Имена переменных - это выражения, которые необходимо оценить.

a + b * c / (d - e)
    2   3    5   6
      4        7
1         8
  9

Итак, ответ на ваш вопрос - №1. Порядок операций определяет форму дерева выражений (то, что является левой стороной дерева, и что является правильным), но сначала левая сторона всегда оценивается (и корень оценивается последним).

Ответ 4

Я бы предположил, что он может оценивать что-то вроде этого, оценивая слева направо.

а + Ь * с/(г-е)

Action           Left Value      Right Value
Start Add        a               b*c/(d-e)
Start Multiply   b               c
Calc Multiply (since it can)    
Start Divide     b*c             (d-e)
Start Subtract   d               e
Calc Subtract
Calc Divide
Calc Add

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

   Add
    /\
   /  \
  a    \
     Divide
       / \
      /   \
     /     \
    /       \
Multiply  Subtract
  /\         /\
 /  \       /  \
b    c     d    e

И я сделал несколько тестов на С# (я знаю, что это не то же самое, но где мои интересы лежат и тесты могут быть легко адаптированы) следующим образом:

        f = 1;
        Console.WriteLine((f=2) + (f) * (f) / ((f) - (f)-1));
        Console.WriteLine(2 + 2 * 2 / (2 - 2 - 1));
        f = 1;
        Console.WriteLine((f) + (f=2) * (f) / ((f) - (f)-1));
        Console.WriteLine(1 + 2 * 2 / (2 - 2 - 1));
        f = 1;
        Console.WriteLine((f) + (f) * (f = 2) / ((f) - (f)-1));
        Console.WriteLine(1 + 1 * 2 / (2 - 2 - 1));
        f = 1;
        Console.WriteLine((f) + (f) * (f) / ((f=2) - (f)-1));
        Console.WriteLine(1 + 1 * 1 / (2 - 2 - 1));
        f = 1;
        Console.WriteLine((f) + (f) * (f) / ((f) - (f=2)-1));
        Console.WriteLine(1d + 1d * 1d / (1d - 2d - 1d));

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

Как можно видеть, аргументы оцениваются по порядку с любым после того, как присваивание равно 2 и до того, как оно будет одним. Поэтому порядок оценки вещей прост в праве, я думаю, но порядок вычислений, как и следовало ожидать, будет таким.

Я предполагаю, что это можно запустить почти с копией и вставкой для тестирования в JAVA...

Здесь могут быть какие-то незаметные предположения, поэтому, если кто-то обнаруживает здесь логические недостатки, пожалуйста, позвоните мне, и я их обработаю.

Ответ 5

Я предполагаю, что ваше выражение будет чем-то вроде

x = a + b * c/(d - e)

оператор равенства имеет право на левый порядок оценки. поэтому сначала будет оценено выражение справа от.

если вы ссылаетесь на этот график приоритета: http://www.java-tips.org/java-se-tips/java.lang/what-is-java-operator-precedence.html

1) скобки будут оцениваться (d-e), скажем (d - e) = f, поэтому выражение тогда становится x = a + b * c/f.

2) Теперь * и/имеют тот же приоритет, но порядок оценки слева направо на * будет сначала оценен, поэтому скажем b * c = g, поэтому выражение примет вид x = a + g/f

3) Теперь/имеет следующий приоритет, так что g/f будет оцениваться так, чтобы сказать h, чтобы выражение получилось x = a + h,

4), наконец оценивая a + h

Ответ 6

В вашем втором вопросе кажется, что Java оценивает часть в скобках как назначение, а не математическое выражение. Это означает, что это не будет выполнять скобки в том же порядке, что и операции в скобках.

Ответ 7

Результаты расчета определяются Оператором порядка приоритета. Таким образом, скобки имеют наивысший приоритет здесь, умножение и деление, следующие самые высокие, и сложение и вычитание наименьшее. Операторы с одинаковым приоритетом оцениваются слева направо. Таким образом, выражение в вопросе эквивалентно выражению:

    a + (b  * c) / ( d - e ))

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

"d-e" не обязательно вычисляется до вычисления "a". Это в значительной степени не имеет никакого значения, если только одна из "переменных" в выражении фактически является функцией. В стандарте Java не указывается порядок оценки компонентов выражения.

Ответ 8

a + b * c / ( d - e )
      1         1
          2
  3

Весь смысл приоритета оператора состоит в том, чтобы преобразовать выражение в дерево синтаксиса. Здесь * и - находятся на одном уровне дерева. Какой из них оценивается первым, не имеет значения для результата и не гарантируется.

Изменить. Извините, меня смутил мой фон. Как указывали другие, в Java есть правило "Оценить левый операнд первым". Применение этого правила к / говорит вам, что сначала оценивается * (ваш первый ответ).