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

Сообщение Re[23]: Carbon от 19.04.2024 9:07

Изменено 19.04.2024 9:08 vdimas

Re[23]: Carbon
Здравствуйте, Sinclair, Вы писали:

S>Ну, например, поставили фейспалм на простое утверждение "UB, из которого язык состоит чуть менее, чем полностью".

S>Вот я вижу, что интовая арифметика используется примерно в каждой первой программе.

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

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

У меня обсуждаемая проблема вечно вылазит в дотнете, бо системное АПИ расписано на знаковых, даже где это выглядит абсурдом.
Причина известна — не во всех языках есть беззнаковый тип, поэтому платформа затачивалась на слабое звено. ))

В итоге, у меня полно кода, дублирующего базовую библиотеку (не просто дублирующего, конечно, многие вещи сделаны легковеснее), но с активным использованием беззнаковых, что резко упрощает целевой прикладной код, расписаный по большей части в беззнаковых.


S>То есть если покрасить исходный код более-менее любого проекта в красный там, где "возможно UB", то его будет больше, чем не красного.


Переполнения принято производить с беззнаковыми, а со знаковыми принято оперировать так, чтобы переполнения не возникало.
В любом случае, реинтерпретация беззнакового как знакового допустима, а вот обратная реинтерпретация — это UB.


CC>>Если ударяться в странные конструкции и потом бороться со странностями compile time вычислений — то гемора будет побольше.

S>Что вы называете "странными конструкциями"? Мы кагбэ просто взяли сложение и сравнение — чо уж тут странного-то?

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


S>Compile-time вычисления сами по себе штука хорошая, поскольку позволяют сильно улучшить перформанс программ без потери общности. Те самые zero-cost abstractions, ради которых люди и выбирают C++ вместо менее упоротых языков.

CC>>Если придерживаться KISS и писать просто и строго по делу, не растекаясь шаблонами по шаблонам — таких проблем не будет.
S>Ооо, мне нравится ваш задор. Давайте попробуем починить UB в функции, написанной просто и строго по делу, безо всяких шаблонов:
S>
S>long avg(long a, long b, long c)
S>{
S>  return (a+b+c)/3; 
S>}
S>


Хороший пример, кстате.
Что в дотнете, что в плюсах такими вещами упражняюсь путём приведения к более широкому типу, поэтому int64 пролетает.

И это известная практика именно с давних времён.
Еще оттуда же практика замены вычисления с плавающей точкой на работу с рациональными дробями (умножение с делением), где для корректной работы аналогично исходные числа сначала расширяются для промежуточных вычислений, потом сужаются.

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


S>А перенос вычислений в рантайм заставил сработать оптимизацию, которая воспользовалась UB в полном соответствии со стандартом языка.


Наверно, наоборот, compile-time вычисления произошли из предположения, что никакого UB не возникает, и тогда результат всегда false.

Кстате, мне Решарпер в C# иногда подсвечивает "тут у вас всегда true" (или всегда false), т.е. намекает на мёртвые ветки кода, хотя не факт.
Спасает то, что компилятор C# и JIT пока мест не делают суровых оптимизаций.
(можно сказать, что компилятор C# не делает никаких оптимизаций от слова вообще, кроме совсем скромных манипуляций локальными переменными)

Когда в дотнете, наконец, случится обещанная тобой еще 20 лет назад суровая оптимизация — напорешься на аналогичные эффекты. ))


CC>>Это архитектурный баг компиляторостроителей, который можно починить но никто не станет, ибо пуристы упрутся рогом.

S>Это интересная гипотеза, но она не подтверждается практикой.

Есть такое.
С++ — это, в первую очередь, инструмент для порождения произвольных по сложности матрёшек абстракций с нулевым пенальти.
Пресловутая "упорость" любителей плюсов сидит только в этом и ни в чём больше, а иначе покажите другой инструмент с тем же свойством, но в чём-то более удобный — и все туда быстренько убегут.

А так-то, забесплатно можно расписать, к примеру, корректное сравнение знакового с беззнаковым:
#include <iostream>

enum int_t : int;
enum uint_t : unsigned int;

bool operator<(int_t a, uint_t b) {
    using uint = unsigned int;
    return int(a) < 0 || uint(a) < b;
}

int main()
{
    auto result = int_t(-42) < uint_t(42);
    std::cout << result << std::endl;
    return 0;
}

(можно в более общем коде для любых интегральных, не хотел просто засорять исходник, показал суть)

Еще популярный сценарий — индексы беззнаковые, а разница индексов знаковая, расписываются операции сложения беззнаковых индексов со знаковой разницей с контролем переполнения.
Re[23]: Carbon
Здравствуйте, Sinclair, Вы писали:

S>Ну, например, поставили фейспалм на простое утверждение "UB, из которого язык состоит чуть менее, чем полностью".

S>Вот я вижу, что интовая арифметика используется примерно в каждой первой программе.

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

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

У меня обсуждаемая проблема вечно вылазит в дотнете, бо системное АПИ расписано на знаковых, даже где это выглядит абсурдом.
Причина известна — не во всех языках есть беззнаковый тип, поэтому платформа затачивалась на слабое звено. ))

В итоге, у меня полно кода, дублирующего базовую библиотеку (не просто дублирующего, конечно, многие вещи сделаны легковеснее), но с активным использованием беззнаковых, что резко упрощает целевой прикладной код, расписаный по большей части в беззнаковых.


S>То есть если покрасить исходный код более-менее любого проекта в красный там, где "возможно UB", то его будет больше, чем не красного.


Переполнения принято производить с беззнаковыми, а со знаковыми принято оперировать так, чтобы переполнения не возникало.
В любом случае, реинтерпретация беззнакового как знакового допустима, а вот обратная реинтерпретация — это UB.


CC>>Если ударяться в странные конструкции и потом бороться со странностями compile time вычислений — то гемора будет побольше.

S>Что вы называете "странными конструкциями"? Мы кагбэ просто взяли сложение и сравнение — чо уж тут странного-то?

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


S>Compile-time вычисления сами по себе штука хорошая, поскольку позволяют сильно улучшить перформанс программ без потери общности. Те самые zero-cost abstractions, ради которых люди и выбирают C++ вместо менее упоротых языков.

CC>>Если придерживаться KISS и писать просто и строго по делу, не растекаясь шаблонами по шаблонам — таких проблем не будет.
S>Ооо, мне нравится ваш задор. Давайте попробуем починить UB в функции, написанной просто и строго по делу, безо всяких шаблонов:
S>
S>long avg(long a, long b, long c)
S>{
S>  return (a+b+c)/3; 
S>}
S>


Хороший пример, кстате.
Что в дотнете, что в плюсах такими вещами упражняюсь путём приведения к более широкому типу, поэтому int64 пролетает.

И это известная практика именно с давних времён.
Еще оттуда же практика замены вычисления с плавающей точкой на работу с рациональными дробями (умножение с делением), где для корректной работы аналогично исходные числа сначала расширяются для промежуточных вычислений, потом сужаются.

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


S>А перенос вычислений в рантайм заставил сработать оптимизацию, которая воспользовалась UB в полном соответствии со стандартом языка.


Наверно, наоборот, compile-time вычисления произошли из предположения, что никакого UB не возникает, и тогда результат всегда false.

Кстате, мне Решарпер в C# иногда подсвечивает "тут у вас всегда true" (или всегда false), т.е. намекает на мёртвые ветки кода, хотя не факт.
Спасает то, что компилятор C# и JIT пока мест не делают суровых оптимизаций.
(можно сказать, что компилятор C# не делает никаких оптимизаций от слова вообще, кроме совсем скромных манипуляций локальными переменными)

Когда в дотнете, наконец, случится обещанная тобой еще 20 лет назад суровая оптимизация — напорешься на аналогичные эффекты. ))


CC>>Это архитектурный баг компиляторостроителей, который можно починить но никто не станет, ибо пуристы упрутся рогом.

S>Это интересная гипотеза, но она не подтверждается практикой.

Есть такое.
С++ — это, в первую очередь, инструмент для порождения произвольных по сложности матрёшек абстракций с нулевым пенальти.
Пресловутая "упорость" любителей плюсов сидит только в этом и ни в чём больше, а иначе покажите другой инструмент с тем же свойством, но в чём-то более удобный — и все туда быстренько убегут.

А так-то, забесплатно можно расписать, к примеру, корректное сравнение знакового с беззнаковым:
#include <iostream>

enum int_t : int;
enum uint_t : unsigned int;

bool operator<(int_t a, uint_t b) {
    using uint = unsigned int;
    return int(a) < 0 || uint(a) < b;
}

int main()
{
    auto result = int_t(-42) < uint_t(42);
    std::cout << result << std::endl;
    return 0;
}

(можно в более общем коде для любых интегральных, не хотел просто засорять исходник, показал суть)

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