А чем тебе типы помогут? Длина слова всё ещё конечная и ты либо возвращаешь результат как значение двойной длины, либо обрабатываешь переполнение, либо говоришь что «у нас тут дополнение до двойки и всё».
Переменные в компайл тайме тебе никто не посчитает, потому что их значения в компайл тайме неизвестны. Только ворнинг можно выдать, что может быть переполнение теоретически.
каков будет результат и какое он имеет отношение к искомой площади прямоугольника?
Есть теорема в дискретной математике о том, что при перемножении чисел размером N разрядов размер результата будет 2*N разрядов. А теперь вспомни как числа вообще представляются в памяти ЭВМ.
Так что ответ таков: используя «длинную арифметику» ты избежишь подобных ситуаций. Иначе никак.
Я даже решил вернуться и проверить либу. Может ли она в нормальную крестовую логику. И оказалось может: https://godbolt.org/z/qxodcg
А то, говно что показывали адепты - это скриптуха мусорная.
А ещё можно реализовать константы и можно будет написать цикл на миллиард с умножением на ноль. Там возможно даже есть поддержка этого. Но что-то я так сразу не нашел.
Ну это без проблем приколхозить даже если нету поддержки через реализацию numeric_limits для констант.
интересно, есть хоть один язык, в котором переполнение при умножение приводит к ошибке компиляции
// запросы к БД LORа, рантайм значения
let overall = get_amount_of_comments("next_time");
let tcoef = get_tupnyak_coefficient("next_time");
// вот тут будет переполнение, как защищаться в компилтайме?
let tamount = overall * tcoef;
>> >> a: 3278934032849022348732897498327323248902859028490582490859042890589204348590234849058240935802438504832837
== 3.278934032849023e105
>> a * a * a
== 1.#INF
NodeJS 12.4.0:
> a = 3278934032849022348732897498327323248902859028490582490859042890589204348590234849058240935802438504832837
3.2789340328490225e+105
> a * a * a
Infinity
Можно (теоретически) ловить и в компайл-тайме. Обсуждали же даже на лоре уже не раз.
uint32_t a, b, c;
a = rand();
b = rand();
if ((a < UINT16_MAX) && (b < UINT16_MAX))
c = a * b; /* все нормально, здесь не может быть переполнения */
} else {
c = a * a; /* а здесь переполнение может быть, (очень) умный компилятор мог бы сделать предупреждение */
}
TypeScript и вроде Hack делают забавную штуку в этом духе. Если родительский класс обычным «if» проверить на instanceof подкласса, то внутри блока переменная будет считаться уже типа подкласса
TypeScript и вроде Hack делают забавную штуку в этом духе. Если родительский класс обычным «if» проверить на instanceof подкласса, то внутри блока переменная будет считаться уже типа подкласса
Это примитивная херня, которая в десять раз проще реализуется, чем показанное выше. Нужна только в скриптухе.
не волнуйся, у тебя оно развалится и на том хелворде. Какой смысл пытаться что-то критиковать будучи адептов пхп? Тут скорее всего говорит не твоё какое-то маня-чувство объективности, а просто попытка защищать свою бездарную веру.
Констрейнты не обязательно тащить далеко, понятно что в некоторых случаях это невозможно. Пусть себе разваливается, можно считать что значение переменной дальше может быть произвольным.
Мы же пишем об инструменте, который подсказывал бы пользователю о потенциальных ошибках, желательно пораньше, в компайл-тайм.
Вот допустим тут c = c * 3 + 1; мы не можем сделать предположений относительно c, но вполне можем предупредить пользователя о том что возможно переполнение
При переполнении ошибки тоже не происходит. Очевидно, что inf это не то, что нужно получить - это уже ошибка, ошибка в полученном результате вычислений.
Типа того. Тут вопрос в том, с какими данными мы работаем - если это какая-то абстрактная математическая ерунда, то наиболее лучшим решением проблемы является расширение типа по мере надобности. Но с абстрактной математической ерундой прогер редко имеет дело. Обычно мы имеем дело с какой-то реальной задачей из реального мира. А там ограничения на размер всплывают вполне естественно - например, редко когда пользователь захочет вводить число размером в миллиард - в реальной жизни такое встречается редко, соответственно, поле для ввода должно быть ограничено. В БД тоже хранятся какие-то реальные данные, и, допустим, число сотрудников в организации, равное MAX_INT, скорее всего, является ошибкой. Код ответа сервера вы вообще вряд ли умножать захотите.
Т.е. на деле, вы всё подряд в храните в простом int просто в силу лени, а по факту - вы имеете налицо изъян в системе типов. В плюсах можно наклепать шаблонов для работы с числами ограниченной размерности - и тогда компилер будет корректно проверять переполнение на этапе компиляции. Просто это будет чуть менее удобно, чем работа с простым инт.
Вот я и смотрю из любопытства в сторону других ЯП.
Я правильно понял идею: на ходу генерить типы LimitedInt<0,100> у которого значения будут только из этого отрезка? Сложение/умножение/деление для обычных арифметических типов запретить. LimitedInt без конструктора, фабричный метод возвращает Option.
Звучит неплохо. Надо будет попробовать запилить. Вопрос только, как запретить операции для встроенных типов и что делать с кучей библиотек, которые с таким типом данных работать не умеют.
Т.е. на деле, вы всё подряд в храните в простом int просто в силу лени
Нет, не от лени. Это в принципе невозможно реализовать на рядовых недоязычках. Для реализации есть два варианта - это актуальный С++ и фп-маргинальщина с зависимыми типами типа кока, идриса. Но маргинальная фп-скриптуха в принципе не является состоятельной для прикладных задач. Что бы там адепты идриса не кукарекали.
Я правильно понял идею: на ходу генерить типы LimitedInt<0,100> у которого значения будут только из этого отрезка?
Эта идея уже показана и реализована выше.
hana::iterate<4>(_ * uint8_t{}, pedantic<int>{})
Т.е. pedantic<int> это значение типа int с диапазоном от min до max.
Далее оно умножается на uint8_t 4 раза. Т.е. диапазон умножается каждую итерацию. Т.е. каждый тип хранит в себе максимальное/минимальное значение которое может быть в него записано.
Сложение/умножение/деление для обычных арифметических типов запретить.
Какая чушь. В чём смысл запрета?
LimitedInt без конструктора, фабричный метод возвращает Option.
Тоже какая-то херня. Причём тут Option?
Звучит неплохо. Надо будет попробовать запилить. Вопрос только, как запретить операции для встроенных типов и что делать с кучей библиотек, которые с таким типом данных работать не умеют.
Ты не сможешь это реализовать - можешь даже не пытаться.
В нормальной языке с этим проблем нет. Там имеет место явное неявное преобразование типов. Выше оно показано. uint8_t воспринимается как любое значение из диапазона uint8_t. ты можешь дописать операцию, которая будет ограничивать значение. Эта операция будет возвращать такой же uint8_t, но его значение будет ограничено условными 0 - 100, а не 0 - 255 как в базовом случае.
Каждая операция увеличивает значение исходя из семантики операции. Так же тебе нужно ввести специальные типы для констант. В нормальном языке это так же без проблем реализуется. 100 -> 100_c.
интересно, есть хоть один язык, в котором переполнение при умножение приводит к ошибке компиляции, а не тихое проглатывание с экзепшеном (паникой и т.п.) в рантайме?
Для этого умножение должно происходить во время компиляции, но, по странному стечению обстоятельств, это обычно делают в рантайме.
Передаем в фабричный метод значение, введенное пользователем, если значение удовлетворяет ограничениям - возращаем Some(LimitedInt<BottomLimit, TopLimit>), если нет - None. В итоге программист будет вынужден проверять, попадает ли значение в интервал.
В чём смысл запрета?
Чтобы не переполняли что попало, а работали только через LimitedInt и другие.
Эта идея уже показана и реализована выше.
Времени прочитать весь тред у меня, конечно же, нет.
Ты не сможешь это реализовать - можешь даже не пытаться.
Для этого умножение должно происходить во время компиляции
Можно на этапе копмиляции ограничиться лимитами значений.
Вообще идея интересная. Но проблема всплывет во время взаимодействия со сторонними библиотеками, которые в эти специальные типы не умеют и используют стандартные типы. В итоге все значения из библиотек придется опять проверять на границы, да и от переполнений в самих библиотеках защититься не получиться.
Проблемы только в том, что компиляция будет занимать очень много времени и непонятно как спасаться от переполнения в библиотеках, работающих на стандартных типах.
Я правильно понял идею: на ходу генерить типы LimitedInt<0,100> у которого значения будут только из этого отрезка?
совершенно верно
Вопрос только, как запретить операции для встроенных типов
я сторонник того подхода, что не все запреты обязательно делать на уровне компилятора, в некоторых случаях достаточно соглашения
на социальном уровне - как раз тот случай
и что делать с кучей библиотек, которые с таким типом данных работать не умеют
на практике, большинства библиотек, кстати, используют числа только для хранения кодов каких-нибудь состояний и размеров - тогда особо и не надо что-то менять
проблема больше актуальна для прикладного кода, а не библиотек
Передаем в фабричный метод значение, введенное пользователем, если значение удовлетворяет ограничениям - возращаем Some(LimitedInt<BottomLimit, TopLimit>), если нет - None. В итоге программист будет вынужден проверять, попадает ли значение в интервал.
Это ничего не даст. Если что-то больше - это так же валидный путь.
Чтобы не переполняли что попало, а работали только через LimitedInt и другие.
Это проблемы твоего восприятия. Они не будут переполнять что попало.
Времени прочитать весь тред у меня, конечно же, нет.
А ещё больше на коках, дальше что? К тому же это неверное. С++ компилируется быстрее любого другого недоязычка. Разница только в том, что кода на С++ попросту больше.
что компиляция будет занимать очень много времени
Нет.
и непонятно как спасаться от переполнения в библиотеках
Это ненужно. Реализовать что-то, что могло-бы работать с этими библиотеками ты итак не сможешь. Толку от этих рассуждений?