Информация об изменениях

Сообщение Re[4]: offsetof() без UB от 24.03.2025 15:24

Изменено 27.03.2025 13:30 kov_serg

Re[4]: offsetof() без UB
Здравствуйте, _NN_, Вы писали:

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

_NN>Есть вариант делать предсказуемое поведение и жаловаться на неоптимальносгенерируемый код.
_NN>Каков ваш выбор ?
Совершенно очевидно, что выбор должен быть явным. Если вы хотите контролировать переполнение явно это указывайте компилятору.
checked int a; // проверяйте of флаг и т.п.
module int b; // обычная модульная арифметика, в ней нет переполнений
saturated int c; // арифметика с насыщением, не коммутативна и т.п. приколы


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

_NN>Пример: https://gcc.godbolt.org/z/11rWEjzdv
Если вы знаете рабочие диапазоны то переполнений не будет.
А если нужна длинная арифметика надо bignum тип иметь. Что бы он мог как строки динамически расширяться при необходимости.

_NN>Расскажите как часто вы пользуетесь определённым переполнением знаковых чисел, а не полагаетесь на неопределённое поведение в случае переполнения.

Приходится преобразовывать к безнаковому, благодоря тому что знаковый считается UB
typedef   signed long long rint;
typedef unsigned long long ruint;
enum { UNSIGNED_OVERFLOW=1,SIGNED_OVERFLOW=2 };
struct rintf { rint r; char f; } ;

rintf adc(int* res,rint a,rint b,char c) {
    char f=0; ruint ua=(ruint)a, ub=(ruint)b, ur=ua+ub+(c&1), us=~(~((ruint)0)>>1);
    if (ur<ua || ur<ub) f|=UNSIGNED_OVERFLOW;
    if ((ua&us)==(ub&us) && ((ua&us)!=(ur&us))) f|=SIGNED_OVERFLOW;
    return rintf{ (rint)ur, f };
}

rintf sbb(rint a,rint b,char c) {
    char f=0; ruint ua=(ruint)a, ub=(ruint)b, ur=ua-ub-(c&1), us=~(~((ruint)0)>>1);
    if (ua<ub || ua<ur) f|=UNSIGNED_OVERFLOW;
    if ((ua&us)!=(ub&us) && ((ua&us)!=(ur&us))) f|=SIGNED_OVERFLOW;
    return rintf{ (rint)ur, f };
}


_NN>Есть библиотека jtckdint, а начиная с C++26 будет Checked integer arithmetic в стандартной библиотеке.

_NN>Уже пользуетесь и пишете скучный и предсказуемый код ?
Да в 146% случаев нужен скучный, предсказуемый код, который выполняет именно то что его попросили.
Re[4]: offsetof() без UB
Здравствуйте, _NN_, Вы писали:

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

_NN>Есть вариант делать предсказуемое поведение и жаловаться на неоптимальносгенерируемый код.
_NN>Каков ваш выбор ?
Совершенно очевидно, что выбор должен быть явным. Если вы хотите контролировать переполнение явно это указывайте компилятору.
checked int a; // проверяйте of флаг и т.п.
module int b; // обычная модульная арифметика, в ней нет переполнений
saturated int c; // арифметика с насыщением, не коммутативна и т.п. приколы


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

_NN>Пример: https://gcc.godbolt.org/z/11rWEjzdv
Если вы знаете рабочие диапазоны то переполнений не будет.
А если нужна длинная арифметика надо bignum тип иметь. Что бы он мог как строки динамически расширяться при необходимости.

_NN>Расскажите как часто вы пользуетесь определённым переполнением знаковых чисел, а не полагаетесь на неопределённое поведение в случае переполнения.

Приходится преобразовывать к безнаковому, благодоря тому что знаковый считается UB
typedef   signed long long rint;
typedef unsigned long long ruint;
enum { UNSIGNED_OVERFLOW=1,SIGNED_OVERFLOW=2 };
struct rintf { rint r; char f; } ;

rintf adc(rint* res,rint a,rint b,char c) {
    char f=0; ruint ua=(ruint)a, ub=(ruint)b, ur=ua+ub+(c&1), us=~(~((ruint)0)>>1);
    if (ur<ua || ur<ub) f|=UNSIGNED_OVERFLOW;
    if ((ua&us)==(ub&us) && ((ua&us)!=(ur&us))) f|=SIGNED_OVERFLOW;
    return rintf{ (rint)ur, f };
}

rintf sbb(rint a,rint b,char c) {
    char f=0; ruint ua=(ruint)a, ub=(ruint)b, ur=ua-ub-(c&1), us=~(~((ruint)0)>>1);
    if (ua<ub || ua<ur) f|=UNSIGNED_OVERFLOW;
    if ((ua&us)!=(ub&us) && ((ua&us)!=(ur&us))) f|=SIGNED_OVERFLOW;
    return rintf{ (rint)ur, f };
}


_NN>Есть библиотека jtckdint, а начиная с C++26 будет Checked integer arithmetic в стандартной библиотеке.

_NN>Уже пользуетесь и пишете скучный и предсказуемый код ?
Да в 146% случаев нужен скучный, предсказуемый код, который выполняет именно то что его попросили.