Re[2]: Откуда эта лютая любовь к знаковым целым?
От: Poopy Joe Бельгия  
Дата: 05.05.20 10:28
Оценка:
Здравствуйте, Homunculus, Вы писали:

H>Во-вторых, иногда нужно не-валидное значение чего-то, что по смыслу неотрицательно. И для этого я лично использую «-1». И если переменная равна ему, то значит значение невалидное. Такая штука с unsigned не проканает, если не вводить всякие magic digits конечно.

std::optional<uint>
Re[3]: Откуда эта лютая любовь к знаковым целым?
От: Stanislav V. Zudin Россия  
Дата: 05.05.20 10:31
Оценка:
Здравствуйте, rg45, Вы писали:

R>Здравствуйте, Stanislav V. Zudin, Вы писали:



SVZ>>К ранеесказанному добавлю ещё один кейс.

SVZ>>Индекс в массиве это полноценная сущность БД. Используется вместо указателей.
SVZ>>В этом случае отрицательные значения используются для обозначения невалидных объектов (у нас это -1) и для каких-нибудь специальных констант.

R>Если вынести за скобки вопросы стиля и дизайна, то операция индексирования применима не только к массивам, но и указателям. Ведь, согласно стандарту, операция индексирования — это просто комбирация опрация сложения указателя и числа с последующим разыменованием. *(p + i), *(i + p), p[i], i[p] — все это равнозначные выражения и индекс вполне может быть отрицательным.


Неее, тут всё гораздо интереснее. Из указателя ты много информации не вытянешь, а вот из индекса в массиве очень даже.
К примеру, у нас в структурах данных некоторые связи не хранятся явно, а вычисляются на основе индекса.

    // ======================================================================
    typedef int BRICK;                  //!< Single mesh cell
    typedef std::pair<BRICK, BRICK> BRICK2; //!< Couple of bricks
    typedef int FACET;                  //!< A facet of the cell (each cell has 6 facets)
    typedef int JOINT;                  //!< Joint between adjacent cells
    typedef unsigned char FILLING;      //!< ID of the Cell filling
    typedef int BRDATAEX;               //!< Reference to the brick additional data
    typedef int JOINTDATAEX;            //!< Reference to the joint additional data
    typedef int FACETREF;               //!< Reference to the list of facets
    typedef float PMLStretch;
    typedef float PMLSigma;
    typedef unsigned char JUNCTYPE;     //!< Тип соединения на Joint (eJunctionFace)
    typedef int UPORT;                  //!< Индекс Micro-port'а
    typedef int UPORTVAL;               //!< Значение Micro-port
    typedef int CORNER;                 //!< Угол ячейки

    // ======================================================================
    enum
    {
        Facet_Brick = 6,                // Число граней у ячейки - для SplitFace могут быть дополнительные subfacets
        Corner_Brick = 8,               // Число углов у ячейки
        UPort_Facet = 2,                // Число микропортов у одной грани или subfacet
        UPort_Brick = 6*2,              // Число микропортов у ячейки - только для Simple, PML, Conformal - больше для SplitFace Brick
        IncFacet_Max = 4                // Макс. число инцидентных граней
                                        // с одной стороны соединения
    };


Вот и пример отрицательных индексов
    enum { NONE = -1, DEAD = -2 };


Вот пример вычисляемых связей между сущностями
    //! Получить первый порт, принадлежащий грани
    inline UPORT Facet2UPort1(FACET f)      { return f * 2; }
    //! Получить второй порт, принадлежащий грани
    inline UPORT Facet2UPort2(FACET f)      { return f * 2 + 1; }


С указателями ты такого не сделаешь.
_____________________
С уважением,
Stanislav V. Zudin
Re[11]: Откуда эта лютая любовь к знаковым целым?
От: Stanislav V. Zudin Россия  
Дата: 05.05.20 10:36
Оценка:
Здравствуйте, T4r4sB, Вы писали:


  жареное кусь
TB>
TB>#include <stdio.h>
TB>main (int t, int _, char *a)
TB>{
TB>return!0<t?t<3?
TB>main(-79,-13,a+main(-87,1-_,main(-86,0,a+1)+a)):1,t<_?main(t+1,_,a) 
TB>:3,main(-94,-27+t,a)&&t==2?_<13?main(2,_+1,"%s %d %d\n"):9:16:t<0?t<-72? 
TB>main(_,t,"@n'+,#'/*s{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l+,/n\ 
TB>{n+,/+#n+,/# ;#q#n+,/+k#;*+,/'r :'d*'3,}{w+K w'K:'+}e#';dq#'l q#'+d'K#!\ 
TB>/+k#;q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# ){nl]!\ 
TB>/n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw' iK{;[{nl]'/w#q#n'wk nw' iwk{KK{nl]!/\ 
TB>w{%'l##w#' i; :{nl]'/*{q#'ld;r'}{nlwb!/*de}'c ;;{nl'-{}rw]'/+,}##'*}\ 
TB>#nc,',#nw]'/+kd'+e}+;#'rdq#w! nr'/ ') }+}{rl#'{n' ')# }'+}##(!!/") 
TB>:t<-50?_==*a?putchar(31[a]):
TB>main(-65,_,a+1):
TB>main((*a=='/')+t,_,a+1):
TB>0<t?main(2,2,"%s")
TB>:*a=='/'||main(0,main(-61,*a,
TB>"!ek;dc i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m .vpbks,fxntdCeghiry" 
TB>),a+1);
TB>}
TB>


TB>А, чорт, это чистый С. На С++17 подобное у вас прокатит?


Мы обфускатором не пользуемся
_____________________
С уважением,
Stanislav V. Zudin
Re[12]: Откуда эта лютая любовь к знаковым целым?
От: T4r4sB Россия  
Дата: 05.05.20 10:38
Оценка:
Здравствуйте, Stanislav V. Zudin, Вы писали:

SVZ>Мы обфускатором не пользуемся


Я не уверен, вроде это живой человек писал, для конкурса какого-то.
Re[13]: Откуда эта лютая любовь к знаковым целым?
От: Stanislav V. Zudin Россия  
Дата: 05.05.20 10:44
Оценка:
Здравствуйте, T4r4sB, Вы писали:

SVZ>>Мы обфускатором не пользуемся


TB>Я не уверен, вроде это живой человек писал, для конкурса какого-то.


Если я не путаю, то перед этим кодом была простынь макросов, переопределяющих практически всё.
_____________________
С уважением,
Stanislav V. Zudin
Re[4]: Откуда эта лютая любовь к знаковым целым?
От: rg45 СССР  
Дата: 05.05.20 10:44
Оценка: +1
Здравствуйте, Stanislav V. Zudin, Вы писали:

SVZ>Неее, тут всё гораздо интереснее. Из указателя ты много информации не вытянешь, а вот из индекса в массиве очень даже.

SVZ>К примеру, у нас в структурах данных некоторые связи не хранятся явно, а вычисляются на основе индекса.


SVZ>Вот пример вычисляемых связей между сущностями

SVZ>
SVZ>    //! Получить первый порт, принадлежащий грани
SVZ>    inline UPORT Facet2UPort1(FACET f)      { return f * 2; }
SVZ>    //! Получить второй порт, принадлежащий грани
SVZ>    inline UPORT Facet2UPort2(FACET f)      { return f * 2 + 1; }
SVZ>


SVZ>С указателями ты такого не сделаешь.


Нет-нет, я не предлагал замеменить индексы на указатели. Пусть будут индексы, и пусть они будут по-прежнему вычисляемыми. Вопрос только в том, к чему эти индексы применяются. Если индексы применяюются к массиву, они действительно не могут быть отрицательными. Но только потому что при этом заведомо возникнет выход за пределы массива, а вовсе не потому, что этого требует операция индексирования. Операция индесирования допускает использование отрицательных индексов, если при этом не возникает выхода за пределы массива — вот та мысль которую я хотел выразить.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[4]: Откуда эта лютая любовь к знаковым целым?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 05.05.20 10:55
Оценка:
Здравствуйте, Marty, Вы писали:

ЕМ>>О да, uint вместо int, ulong вместо long — это ж пальцы отобьешь...


M>Таких типов нет, если что


У кого нет, а у кого и есть.

M>Может унутре используются. То, что к тебе отрицательные значения не попадают, это еще ничего не значит


Где "унутре", и как они могут ко мне "не попадать"? Вы о чем вообще? Пример можно?
Re[2]: Откуда эта лютая любовь к знаковым целым?
От: Reset  
Дата: 05.05.20 10:57
Оценка: 6 (1) +2
V>в добавку к уже сказанному:

V>
V>#include <iostream>

V>int main() {
V>  std::cout << ( -1 / 1u ) << std::endl;
V>  std::cout << ( -1 < 1u ) << std::endl;
V>  return 0;
V>}
V>


Чтобы такой код не вызывал шевеления волос в разных местах, достаточно один раз разобраться с правилами приведения типов в выражениях C++. Правила эти почти примитивные: выражение тоже имеет тип, перед его вычислением тип обоих аргументов приводится к "большему", минимальный тип int, беззнаковый "больше" знакового.

Преобразование встроенных типов в операторах

Выражениям так же как и значениям в C++ приписывается некоторые типы. Например, если a и b — это переменные типа int, то выражения (a + b), (a — b), (a * b) и (a / b) тоже будут иметь тип int.

Важно всегда понимать, какой тип у выражения, которое вы написали в программе. Давайте проиллюстрируем это на следующем примере:

int a = 20;
int b = 50;
double d = a / b;  // d = 0, оба аргумента целочисленные, а значит деление целочисленное
Как исправить этот код, чтобы получить вещественное значение в переменной d?

Для этого хотя бы один из аргументов оператора деления должен иметь типа double. Этого можно добиться при помощи уже известного нам оператора приведения типов:

double d = (double)a / b;  // d = 0.4

Почему это сработало? Дело в том, что операторы для встроенных типов C++ всегда работают с одинаковыми типами аргументов. Если аргументы имеют разные типы, то происходит преобразование типов (promotion).

Правило преобразования встроенных типов в операторах

Рассмотрим выражение (a + b), где вместо '+' может стоять любой другой подходящий оператор.
Если один из аргументов имеет числовой тип с плавающей точкой, то второй аргумент приводится к этому типу (например, при сложении double и int значение типа int приводится к double).
Если оба аргумента имеют числовой тип с плавающей точкой, то выбирается наибольший из этих типов (например, при сложении double и float значение типа float приводится к double).
Если оба аргумента целочисленные, но их типы меньше int, то оба аргумента приводятся к типу int (например, при сложении двух значений типа char они оба сначала приводятся к int).
Если оба аргумента целочисленные, то аргумент с меньшим типом приводится к типу второго аргумента (например, при сложении long и int значение типа int приводится к long).
Если оба аргумента целочисленные и имеют тип одного размера, то предпочтение отдаётся беззнаковому типу (например, при сложении int и unsigned int значение типа int приводится к unsigned int).
Несколько важных следствий
Следите за тем, какие типы участвуют в выражении, от этого может зависеть его значение.
Не стоит использовать целочисленные типы меньше int в арифметических выражениях, они всё равно будут приведены к int.
Не стоит смешивать unsigned и signed типы в одном выражении, это может привести к неприятным последствиям.
Для иллюстрации последнего следствия давайте рассмотрим следующий пример:
unsigned from = 100;
unsigned to = 0;
for (int i = from; i >= to; --i) { .... }
Сколько итераций сделает этот цикл? На самом деле этот цикл — бесконечный: в условии цикла проверяется i >= to, где тип i — int, а тип to — unsigned int. Так как операторы всегда применяются к одинаковым встроенным типам, то в данном случае значение переменной i (в соответствии с правилом 5) будет преобразовано к unsigned int, а значения этого типа всегда неотрицательны, т.е. >= 0. Другими словами, когда пременная i станет равной -1, то в условии будет проверяться (unsigned)-1 >= 0, где (unsigned)-1 = UINT_MAX (UINT_MAX — максимальное значение, которое может принимать переменная типа unsigned int).


Взято отсюда.

После этого любые подобные примеры являются четким критерием того, что разработчик не знаком с довольно простыми правилами преобразования типов в выражении. Просто дайте ему почитать приведенные тут правила (любой поймет и запомнит минут за 5-10).
Re[3]: Откуда эта лютая любовь к знаковым целым?
От: T4r4sB Россия  
Дата: 05.05.20 11:01
Оценка:
Здравствуйте, Reset, Вы писали:

R>Чтобы такой код не вызывал шевеления волос в разных местах, достаточно один раз разобраться с правилами приведения типов в выражениях C++. Правила эти почти примитивные: выражение тоже имеет тип, перед его вычислением тип обоих аргументов приводится к "большему", минимальный тип int, беззнаковый "больше" знакового.


Ответ неверный, потому что он не учитывает человеческий фактор.
Правильный ответ: включаем /W4, подсветив все неявные касты, и убираем их из программы нафиг.
Re[11]: Откуда эта лютая любовь к знаковым целым?
От: rg45 СССР  
Дата: 05.05.20 11:08
Оценка:
Здравствуйте, T4r4sB, Вы писали:


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


TB>Да, я уверен, что цикл с кулхацкерским заголовком (а такое использование for, в котором используются побочные эффекты условия выхода, можно назвать только кулхацкерским) бы не прошёл у нас ревью.


А не может случиться так, что "кулхацкерским" этот "заголовок" является только потому, что он не вписывается в твое исходное утверждение? Я тебе из собственного опыта могу сказать, что иногда гораздо дешевле просто признать, что ошибался, чем отстаивать заведомо ошибочное утверждение. Смотри вон, сколько ты уже энергии потратил
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[12]: Откуда эта лютая любовь к знаковым целым?
От: T4r4sB Россия  
Дата: 05.05.20 11:11
Оценка:
Здравствуйте, rg45, Вы писали:

R>А не может случиться так, что "кулхацкерским" этот "заголовок" является только потому, что он не вписывается в твое исходное утверждение?


Нет, он кулхацкерский потому, что использует for не по назначению. У нормального for первое выражение — это инициализацпия, второе — это условие без побочных эффектов, а третье — это побочные эффекты. Всё остальное — это кулхацкерство.
Re[2]: Откуда эта лютая любовь к знаковым целым?
От: vopl Россия  
Дата: 05.05.20 11:15
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Ты на беззнаках даже тупо от ЭН до нуля проитерироваться не можешь без дополнительного бубна.


Два наисложнейших бубена, оба без UB:

#include <iostream>
using namespace std;

int main()
{
    for(unsigned u{5+1}; u-->0; )
        cout << u << endl;

    for(unsigned u{5}; u<=5; --u)
        cout << u << endl;

    return 0;
}
Re[2]: Откуда эта лютая любовь к знаковым целым?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 05.05.20 11:16
Оценка: +1 -1
Здравствуйте, Nuzhny, Вы писали:

N>1. Про итерирование от N до 0 уже написали, но повторюсь.


А оценка того, насколько часто это требуется, будет?

N>2. OpenMP. Неожиданно, но Майкрософт в своём компиляторе поддерживает только очень старую версию 2.0, в ней индексы для for могут быть только знаковыми.


С какой целью хоть OpenMP, хоть что другое, использует знаковые индексы?

N>3. Адресная арифметика вся знаковая, родной тип ptrdiff_t. То есть если я захочу сделать размеры картинки (ширину и высоту) сделать, например, size_t и ходить по изображению по байтам, то мне всё равно надо переходить к ptrdiff_t, чтобы компилятор не ругался.


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

N>4. Внезапно оказывается, что многие типы становятся знаковыми, хотя по логике они такими быть, на первый взгляд, не могут. Например, детектирую я пешеходов и авто на кадре. И их левая координата уходит в минус, если в кадре видна только часть автомобиля.


И что здесь "внезапного"? Так всегда было, есть и будет. Для координат в пределах кадра — беззнаковые величины, для координат в абстрактной системе — знаковые.

N>5. Далее знаковые становятся удобнее, когда происходит преобразование в другие системы координат. В твоей экранной системе координаты только положительные, ты рисуешь график в декартовой и числа внезапно становятся отрицательными.


На это есть преобразования типов.

N>6. Даже яркость пикселя, которая чаще всего от 0 до 255 и представлена в uchar при манипуляциях с яркостью легко вылезает за пределы типа вверх и вниз


Тоже не в кассу, здесь разрядности типа хранения и типа обработки разные.

N>Получается, что большая часть моих кейсов сводится к тому, что естественные ограничения на беззнаковость идут к херам при вычислениях с этими типами. Насколько размер одного контейнера больше второго? Нельзя просто так взять и вычесть! Надо написать if (v1.size() > v2.size())...


Когда подобных операций много, и преобразования сильно загромождают — не вопрос. Я прежде всего о тех случаях, когда ничего подобного не происходит, но типы используются знаковые, тем более — в примерах книг и статей.
Re[3]: Откуда эта лютая любовь к знаковым целым?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 05.05.20 11:18
Оценка:
Здравствуйте, Философ, Вы писали:

Ф>Особенно, когда отрицательным значением задаются размеры в структурах:


В чем разница между передачей через поле структуры и через параметр функции?

Ф>мы ведь не можем быть уверены, что переданный буфер не имеет отрицательного размера.


Что значит "буфер отрицательного размера"? Каким образом можно создать такой буфер?
Re[13]: Откуда эта лютая любовь к знаковым целым?
От: rg45 СССР  
Дата: 05.05.20 11:19
Оценка: -1
Здравствуйте, T4r4sB, Вы писали:

TB>Нет, он кулхацкерский потому, что использует for не по назначению. У нормального for первое выражение — это инициализацпия, второе — это условие без побочных эффектов, а третье — это побочные эффекты. Всё остальное — это кулхацкерство.


Откуда ты черпаешь свою уверенность? Каковы твои источники? Постфиксный инкремент, походу, отменять пора, как "кулхацкерский"?
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[3]: Откуда эта лютая любовь к знаковым целым?
От: T4r4sB Россия  
Дата: 05.05.20 11:25
Оценка: +1
Здравствуйте, vopl, Вы писали:

V>Два наисложнейших бубена, оба без UB:


V> for(unsigned u{5+1}; u-->0; )

V> cout << u << endl;

Использование второго выражение в заголовке цикла не по назначению

V> for(unsigned u{5}; u<=5; --u)

V> cout << u << endl;

Противоестественная семантика условия выхода. Да, я знаю, как работает переполнение для беззнаковых. Всё равно это лажа. К тому же она не проканает, когда надо проитерироваться от ЭН до ИКС, где ИКС это число 0 нуля включительно.

Это и есть бубны.
Re[4]: Откуда эта лютая любовь к знаковым целым?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 05.05.20 11:26
Оценка:
Здравствуйте, Marty, Вы писали:

M>От N-1 до нуля включительно — на самом деле хотелось бы весьма часто.


На многих архитектурах вполне годится "for (uint i = N; int (i) >= 0; --i)". Если нужно совсем переносимо, то можно считать от N до 1, используя i-1, разница в объеме и скорости ничтожная.

M>Имхо это как раз стандартной библиотекой и привито


Я очень мало пользуюсь стандартной библиотекой, и только сишной. std, boost и прочие вообще никогда не использовал.

ЕМ>>Случаев, подобных упомянутому, или неочевидных ошибок за это время были единицы.


M>Так наверно преобразования возникали как раз из-за безнаковости плюсовых привычек


До "плюсовых" привычек у меня несколько лет были сишные, но я и там никогда не использовал знаковых типов для натуральных чисел.
Re[14]: Откуда эта лютая любовь к знаковым целым?
От: T4r4sB Россия  
Дата: 05.05.20 11:27
Оценка:
Здравствуйте, rg45, Вы писали:

R>Постфиксный инкремент, походу, отменять пора, как "кулхацкерский"?


Ну допустим, что i++ в середине выражения ещё можно считать стандартной практикой, если i больше нигде в выражении не используется.
А какие-нибудь c=a+=b уже точно нафиг.
Re[4]: Откуда эта лютая любовь к знаковым целым?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 05.05.20 11:30
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>От нуля до N-1. На беззнаковых такой перебор записывается довольно неестественно.


"for (uint i = 0; i < N; ++i)". В каком месте неестественность? Или оно должно работать и при N = 0? Если так, то что обозначает 0-1 = -1, какой смысл несет это значение?
Re[2]: Откуда эта лютая любовь к знаковым целым?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 05.05.20 11:32
Оценка:
Здравствуйте, Stanislav V. Zudin, Вы писали:

SVZ>Индекс в массиве это полноценная сущность БД. Используется вместо указателей.

SVZ>В этом случае отрицательные значения используются для обозначения невалидных объектов (у нас это -1) и для каких-нибудь специальных констант.

По-хорошему, все эти специальные константы неплохо бы именовать. А какая разница, именовать -1 или uint_max?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.