У меня любопытный вопрос (задаю его себе, читая грубый фрагмент кода). Посмотрим на выражение:
double a = c*d*e*2/3*f;
где c, d, e, f - инициализированные переменные типа double. Гарантирует ли стандарт, что он будет рассматриваться как c*d*e*2 (двойной результат), затем разделен на 3 и умножен на f (или подобное поведение). Очевидно, что 2/3, равные 0, нежелательны.
В каком параграфе стандарта это определяется?
@jww сузил вопрос, целью было фактическое положение в стандарте, которое будет применяться здесь
Как ранее отмечалось YSC, математическая терминология здесь используется очень слабо или даже неправильно. Коммутативность означает переупорядочивание операнды, а не операций, например a*b против b*a. Вы имеете в виду ассоциативность, например (a*b)*c против a*(b*c). Математически умножение является ассоциативным и коммутативным, но умножение FP не ассоциативно из-за потенциально различного округления промежуточных результатов. Однако деление не является ни ассоциативным, ни коммутативным. a*b/c - это особый случай, когда деление является математическим. ассоциативный с умножением.





На основе стандарт
[intro.abstract] - Примечание 7 (ненормативный):
Operators can be regrouped according to the usual mathematical rules only where the operators really are associative or commutative.
Математическое правило для MDAS - слева направо (с учетом ассоциативности и приоритета операторов). Итак, это оценивается следующим образом:
(((((c * d) * e) * 2) / 3) * f)
Раздел, на который вы ссылаетесь, посвящен объектам функций. Этой цитаты нет и рядом.
Я знал, что это должно было быть там! ваша ссылка указывает не на тот абзац, это в п.4.1.1. Но зная формулировку, нашел.
Теперь это раздел 1.7, которого просто не существует. Просто поместите в свой ответ раздел название, то есть [chpater.subchapter.etc] с номером абзаца. Это не слишком сильно повлияет на изменения и подтвердит ваш ответ в будущем. К тому же оно информативнее, чем одеяло «эталон».
Это [intro.abstract]/7, но это ненормативное примечание (и вы его упоминаете в должен). Должна существовать лучшая цитата.
@YSC Полагаю, я должен быть параноиком и все равно рекомендовать использовать (2.0/3.0)*c*d*e*f
@Swift нет, вам не следует, но вы также должны писать код с первой целью, чтобы максимизировать читаемость. Так что да, напишите что-нибудь вроде double a = ((c*d*e*2)/3)*f;, чтобы даже новичок мог понять, что происходит. Однако будьте осторожны, умножение с плавающей запятой не ассоциативно ((a*b)*c != a*(b*c)).
@YSC - это не вопрос удобочитаемости, это вопрос правильного выполнения на множестве довольно экзотических платформ с неосновными компиляторами (например, это должен быть C++ 1x, но у него нет constexpr), с жестким ограничением, что это невозможно для тестирования на целевых платформах (их еще нет и с ними невозможно связаться). другие значения в настоящее время не упорядочены ни в каком отношении к ожидаемым значениям, в то время как порядок величины фактически был известен (это часть матрицы фильтра шума)
@Swift - это всегда читабельность. Если вы пренебрегаете этим, вы получите баги, баги, которые трудно исправить.
@YSC, тогда вы будете в ужасе от большей части кода, это приложение реального времени без каких-либо выделений и много чего сделано в глобальных переменных. И единственный способ увидеть, как происходит ошибка, - либо смоделировать ее теоретически (без автоматизированных моделей оборудования), либо запустить и увидеть, как взрываются многомиллионные объекты [X)]. код был как раз результатом расширения матрицы до множества линейных выражений. Я уже обнаружил несколько несоответствий и ошибок сравнений со знаком / без знака при преобразовании из геоцентрического в NED.
@Swift Я видел такой код. Это должно быть не потому, что оно существует.
@YSC иначе писать запрещено. по сути, "новый" оператор или все, что его использует, запрещено. Это приложения реального времени ..
@Swift Откуда взялся этот new? Я говорил о удобочитаемости, а не о динамическом размещении. Я работал над встроенными системами, и это не потому, что у вас есть особенности, которые не позволяют писать легко читаемый код.
@YSC в отношении читаемости материала, поскольку сопоставление его с канонической формой 2.0 / 3.0 по отдельности будет более читаемым, потому что 2/3 присутствует в формуле матрицы фильтра в начале. aбc * d впереди появился по причине, известной только автору (которого здесь больше нет). кроме того, эта функция получила только десять страниц документации
Одним словом - да.
Искомое свойство называется ассоциативностью операторов. Он определяет, как операторы с одинаковым приоритетом (например, * и /) группируются и упорядочиваются при отсутствии скобок.
В вашем случае и *, и / имеют одинаковый приоритет и оба являются левоассоциативными, то есть они оцениваются слева направо. Это означает, что c будет умножен на d, затем результат на e, затем результат на 2 (что будет сделано с помощью арифметики с плавающей запятой, поскольку вы умножаете double на литерал int), а затем разделите на 3 (опять же, используя арифметику с плавающей запятой) и, наконец, умноженное на f.
Дополнительную информацию см. В эта страница cppreference.
И *, и / имеют одинаковый приоритет и ассоциативны слева направо, это означает, что
a*b*c*d
разбирается как
((a*b)*c)*d
и то же самое верно, если вы замените любой из * на /.
Источник: http://en.cppreference.com/w/cpp/language/operator_precedence
Операции с плавающей запятой @Swift не коммутативны из-за ошибок округления промежуточных результатов. Если вы используете gcc, вы можете использовать --ffast-math, чтобы игнорировать это.
@mch Я думаю, вы имели в виду ассоциативный;) Конечно a*b == b*a, но (a*b)*c != a*(b*c).
@mch стою поправлюсь! И нет, ни gcc, ни даже двоичный код IEEE-754. десятичная версия, 192 бит.
Возможный дубликат Математический порядок работы C / C++, Каковы правила, регулирующие смешанные вычисления с одинарной и двойной точностью в C++?, Порядок операций для максимальной точности и т. д.