LINUX.ORG.RU
Форум — Talks  

Оказывается, я всегда неверно знал порядок сишной арифметики

 , вычитание,


0

5
#include <stdio.h>

int main(void) {
  unsigned x;
  double f;
  x = 100;
  f = -x/10.0;
  printf("%1.6f\n", f);
  return 0;
}

как думаете что выведет эта программа?

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

А правильно вот так:

  f = -(x/10.0);
или так
  f = 0-x/10.0;

Я несколько разочарован данной неконсистентностью оператора вычитания, хотя наверно и понятно откуда так получилось: выражение типа -1.0/2.0 должно считаться в одно действие (деление -1 на 2) а не в два (сначала 1/2 а потом сменить знак), а поведение минуса перед числовым литералом и перед переменной сделали единообразным чтобы от препроцессора дополнительные сюрпризы не обнаруживались.

А вы знали об этом? Или может быть вообще не задумывались? (тут три варианта: знал правильно, знал неправильно, не думал)

★★★★★

Последнее исправление: CrX (всего исправлений: 2)

как думаете что выведет эта программа?

Сперава подумал, что -10, ведь (-100)/10.0 = -10. Потом заметил, что unsigned, понял, что ничего хорошего, потому что нельзя сделать -x. Что унарный минус мог бы применяться после деления даже в голову не пришло. Для меня оно как-то интуитивно именно так, как есть, если бы где-то было так, как хочешь ты, я бы сильно удивился.

CrX ★★★★★
()
Ответ на: комментарий от CrX

Ну, я унарный минус всегда рассматривал как бинарный с неявным нулевым аргументом слева.

И думал например, что 1*-x - синтаксическая ошибка, а оказывается так тоже можно писать (но я не буду, это некрасиво).

firkax ★★★★★
() автор топика
Последнее исправление: firkax (всего исправлений: 1)

ты упрлс?!

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

а вот то что унарные в сяшке как слева так справа - вот это анафема вынуждающая скобит - будь они все с одной стороны их порядок был бы однозначно (от арга )

qulinxao3 ★☆
()

Видел этот тред на одной странице.

ZenitharChampion ★★★★★
()

Ты так ничего не понял. ж) Беда не в -, который в данном случае ассоциативный, а в unsigned.

beastie ★★★★★
()
Ответ на: комментарий от firkax

Ну, я унарный минус всегда рассматривал как бинарный с неявным нулевым аргументом слева.

Ну вот он и корень проблемы. Унарные операторы не являются бинарными с нулевым аргументом слева, они отдельно. Для меня они как-то всегда воспринимаются как бы «приклеенными» к числу/переменной, то есть имеющими наивысший приоритет. Я бы, кстати, даже записал это как -x / 100, скорее всего.

CrX ★★★★★
()
Ответ на: комментарий от firkax

я унарный минус всегда рассматривал как бинарный с неявным нулевым аргументом слева.

Assumption is the mother of all fuck-ups. ¯\_(ツ)_/¯

А с арифметикой тут всё в порядке. Skill issue.

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 1)
Ответ на: комментарий от beastie

Нет, с unsigned всё норм, а -x/10.0 (точнее похожее выражение) я написал как раз с расчётом что оно сначала скастует unsigned в double а потом уже будет добавлять минус. Если бы, как ты вероятно хочешь представить, я просто забыл про знаковость переменной, я бы не писал там .0 после второго числа.

firkax ★★★★★
() автор топика
Ответ на: комментарий от CrX

Не, это именно с минусом такое было. То что ! и ~ не обобщаются на бинарные это конечно понятно. А минус так похож на бинарный и вот. Кстати, когда я писал сишный компилятор (недоделал), в нём минус так же себя вёл (захватывая операнды из под деления), ну я там делал парсер по формальным правилам, а вот это ощущение что минус всегда бинарный - оно было неформальным, и я как-то ни разу не догадался их друг с другом сопоставить, думая что и так всё понятно.

firkax ★★★★★
() автор топика
Последнее исправление: firkax (всего исправлений: 1)

Всегда предпочитаю расставить лучше побольше скобок, когда речь идёт о приведении типов, чем думать какой он порядок себе понапридумывает.

unDEFER ★★★★★
()
Ответ на: комментарий от beastie

Мне кажется ты считаешь что я с тобой спорю касательно того как должен работать минус. А я это делать не собирался. И приоритеты операторов я разумеется видел и даже два (или больше?) раза реализовывал.

firkax ★★★★★
() автор топика

А вы знали об этом? Или может быть вообще не задумывались? (тут три варианта: знал правильно, знал неправильно, не думал)

не думал т.к. в голову не приходило подобное творить.

anc ★★★★★
()
Ответ на: комментарий от firkax

Да я и не спорю с тобой. Просто инфа для контекста. В остальном, каждый наверное тут когда-то обжёгся, но это было лет 30 назад и все уже давно забыли и запомнили, хотя бы когда калькулятор на yacc писали.

beastie ★★★★★
()

А вы знали об этом?

У унарного минуса приоритет высокий.

Как-то писал программу, которая через алгоритм Дейкстры переводила выражения в ПОЛИЗ. Унарный минус заменял на ‘@’ и после этого как-то очевидно, что у унарного минуса высокий приоритет.

Jullyfish ★★
()

unsigned x; -x

/0 и этот человек говорит мне не писать ОС. Да тебе вообще к компьютерам подходить нельзя.

ant1 ★
()
Ответ на: комментарий от ant1

Если ты хотел сказать, что к unsigned числам нельзя применять унарный минус, то ты совершенно не прав. Применять можно, и это много где используется. Но в данном случае это не то, что планировалось.

firkax ★★★★★
() автор топика
Ответ на: комментарий от unDEFER

Всегда предпочитаю расставить лучше побольше скобок, когда речь идёт о приведении типов, чем думать какой он порядок себе понапридумывает.

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

К слову неужели код из темы не даёт warning? Унарный минус к unsigned - я думаю чаще ошибка чем осмысленный код.

GPFault ★★★★
()

А вы знали об этом?

Всегда знал, что unsigned от лукавого

ya-betmen ★★★★★
()
Ответ на: комментарий от GPFault

Вот и касательно расстановки кучи скобок, и касательно вездесущих лишних тайпкастов имею мнение что лучше не засорять экран. С тайпкастами логика простая: если одно из чисел вещественное то второе само таким же станет. Именно поэтому тут не -(double)x/10, а -x/10.0 - второе короче и нагляднее, однако теперь буду знать что скобок всё-таки не избежать, и писать надо -(x/10.0) - это всё ещё смотрится значительно лучше явного тайпкаста.

Что касается варнинга, то нет, не даёт. Унарный минус к unsigned это операция извлечения дополнительного кода, вполне легитимная. Хотя, учитывая какие ненужные варнинги иногда встречаются, может где-то и такой есть. Но в gcc -Wall -Wextra его нет. Более того, даже если ты напишешь x = -10; для unsigned x, он просто молча скастует -10 к unsigned-у и тоже никаких варнингов не напишет. Тут я бы уже рассмотрел возможность опционального варнинга кстати, но в gcc -Wall -Wextra его всё ещё нет.

firkax ★★★★★
() автор топика
Ответ на: комментарий от firkax

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

Можно-можно, только потом каждая вторая софтина на си дырявая, а каждая первая бажная. Я знаю единственное применение и там лучше использовать ~.

ant1 ★
()
Ответ на: комментарий от ant1

У тебя может и дырявые, у меня нет. Надо просто понимать что именно делает каждая операция и всё будет норм, а не писать код от балды.

Про необходимость использования это другой вопрос. Я и правда не могу особо вспомнить когда он был нужен.

firkax ★★★★★
() автор топика

Природа до того очистилась, что сишники открывают для себя Си уже без моей помощи. Даже вбрасывать про UB уже не требуется ^___^

yorshka
()
Ответ на: комментарий от firkax

конкретно для взятия отрицания от unsigned можно по сути совместить каст и взятие отрицания через -1. * x что даст довольно наглядный результат и без скобок.

а для всего что касается неарифметической работы с числами предпочитаю оставаться в рамках битовых операций ~ ^ & | записи с ними позволяют при чтении быстро понять что тут «не вычислительная арифметика» а другие по сути операции

GPFault ★★★★
()
Ответ на: комментарий от ant1

Вообще-то в большинстве случаев при использовании унарного минуса над целыми имеет смысл его использовать именно над unsigned, поскольку -INT_MIN это UB.

(Как, например, вы реализуете abs() из стандартной библиотеки?)

Поэтому как раз у вас будут баги и дыры.

shdown ★★
()
Последнее исправление: shdown (всего исправлений: 1)

А вы знали об этом?

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

И вообще есть, компиляторы, которые не умеют 2 действия в одной строке.

yax123 ★★★★★
()

оператора вычитания

Нет, здесь это не вычитание, а унарный минус, который имеет гораздо больший приоритет чем арифметика и меняет знак на противоположный, НО x типа unsigned, отсюда и получается такой результат.

Для 32-битного unsigned -x вычисляется по формуле «2^32 - x».

В сабжевом случае достаточно было сделать так:

  f = -(double)x/10.0;

saahriktu ★★★★★
()

А правильно вот так:

Разумеется НЕТ. Правильно так как делает компилятор. Вы можете быть разочарованы исключительно собой.

P.S. Какой самогон надо пить и что курить, чтобы написать вот такой код? Я бы такого с позволения сказать программиста уволил в первый день, когда обнаружил такой код. И мне было бы без разницы что он там себе вообразил. При чем для этого достаточно всего 2х строчек кода:

unsigned x;

double f;

lefsha ★
()
Последнее исправление: lefsha (всего исправлений: 3)

unsigned x;

Play stupid games, win stupid prizes.

peregrine ★★★★★
()

Вот что говорит Opus 5.5:

Приоритет тут ни при чём, проблема в unsigned. Унарный минус в C всегда был приоритетнее любого бинарного оператора, это есть в первой же таблице приоритетов в K&R. <...>

Теория про литералы — фантазия. В C нет отрицательных числовых литералов вообще. -1.0 — это унарный минус, применённый к литералу 1.0, то есть ровно те же два действия, которые автор считает «неконсистентными». Никакой особой обработки «минуса перед литералом» препроцессор не делает. <...>

Так что мой ответ на вопрос: знал правильно. А «неконсистентность вычитания», которой автор разочарован, на самом деле неконсистентность его модели в голове, а не языка.

slyjoeh ★★★★
()
Ответ на: комментарий от dimgel

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

qulinxao3 ★☆
()
Ответ на: комментарий от shdown

Вам нерусским по серому написано UNsigned. Это значит БЕЗзнака. Что вы там пытаетесь натянуть..

-INT_MIN это UB на 32 битной системе в любой ситуации.

Никакой «стандартной» библиотеки у си нет. Стандарт не гарантирует имплементацию. В моей библиотеке унарный минус к unsigned не приставляют. И вам не советую.

ant1 ★
()
Ответ на: комментарий от ant1

Какое-то воинствующее невежество.

https://github.com/torvalds/linux/blob/8a36f53fd5ec3eff01f1b637d42cbdc3821d04...

https://github.com/torvalds/linux/blob/8a36f53fd5ec3eff01f1b637d42cbdc3821d04...

https://github.com/bminor/glibc/blob/04e750e75b73957cf1c791535a3f4319534a52fc... и ниже

https://github.com/gcc-mirror/gcc/blob/a099b3ef74189650f81b99cc1aa149d78aec11...

https://github.com/gmp-mirror/gmp/blob/4b3b66d3328e390af894b97e0438aed90c3305...

И вы не ответили на вопрос: как вы реализуете аналог abs() из стандартной библиотеки? Вот вам прототип:

unsigned abs(int x);

Reference implementation нужно, или сами поймёте, что оно должно делать?

shdown ★★
()
Последнее исправление: shdown (всего исправлений: 1)

Не задумывался.

Вообще если бы я делал язык программирования, я бы запретил подобные операции. По-мне всё нужно брать в скобки всегда без исключений. Тогда таких вопросов бы не возникало. Приоритет операторов слишком запутанная штука, чтобы его помнить. А когда он ещё и в разных языках разный, тут вообще труба.

vbr ★★★★★
()
Ответ на: комментарий от shdown
unsigned abs(int x) {
  if (x >= 0) {
    return (unsigned) x;
  }
  if (x == INT_MIN) {
    return (unsigned) INT_MAX + 1;
  }
  int nx = -x;
  return (unsigned) nx;
}

Вроде можно и отрицательный int кастовать в unsigned. Тогда, думаю, можно так написать:

unsigned abs(int x) {
  unsigned ux = (unsigned) x;
  if (ux <= INT_MAX) {
    return ux;
  }
  return -ux;
}

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

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 3)
Ответ на: комментарий от vbr

всё нужно брать в скобки всегда без исключений

Полностью согласен. В лиспах такой тряхомудии с приоритетами в принципе не бывает.

Nervous ★★★★★
()
Последнее исправление: Nervous (всего исправлений: 1)
Ответ на: комментарий от Nervous

Лисп это вообще гениальная концепция на все времена. Если к нам прилетят инопланетяне, у них будет софт на лиспе.

vbr ★★★★★
()
Ответ на: комментарий от dimgel

Занимательный факт: из двух минусов получить плюс можно, а из двух плюсов минус - нет.

bbc69
()
Последнее исправление: bbc69 (всего исправлений: 1)
Ответ на: комментарий от madcore

Аналог чего? Просили реализовать функцию с прототипом unsigned abs(int x);

vbr ★★★★★
()

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

PcheloBiaka ★
()
Ответ на: комментарий от vbr

Вместо ux<=INT_MAX лучше сравнивать x>=0 - тогда не придётся привязываться к внешним константам, и выглядеть будет проще. А вот первый вариант наоборот ещё более запутанный.

firkax ★★★★★
() автор топика
Ответ на: комментарий от shdown

До этого момента никакой воинственности не было, а теперь я понял что ты дурак :D

Посмотри хотя бы в свою «стандартную» библиотеку, воен, если ума не хватает abs написать.

Развелось детей на лоре…

ant1 ★
()
Последнее исправление: ant1 (всего исправлений: 1)
Ответ на: комментарий от qulinxao3

али

Качни cc7a (он только под винду, но под wine работает) и попробуй в хелловорд написать, что-то вроде:

a = b+c*d-abs(e);

yax123 ★★★★★
()
Ответ на: комментарий от vbr
unsigned abs(int x) {
    return x < 0 ? ~(unsigned)x + 1 : (unsigned)x;
}
ant1 ★
()
Ответ на: комментарий от qulinxao3

ты эта! … бямка пояснит

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

yax123 ★★★★★
()
Вы не можете добавлять комментарии в эту тему: только для зарегистрированных, score>=50.