Pull to refresh

Comments 108

Когда исходники были закрытыми подобные посты читать было интереснее. Раньше ведь как:
1) Присоединился дебаггером к процессу
2) *какая-то магия с ассемблером*
3) Профит!
А сейчас что? Скачал исходники, поставил току останова и отладил. Скукота!
Так ведь исходники могут не совпадать с версией, которая уже установлена.
Компилировать чужие исходники (иногда свои) бывает очень трудно.
И надо знать, куда ставить breakpoint.
Я писал это с некоей долей иронии если что)
Да зачастую в дебаггере с дизассемблером баг найти не в пример проще, чем в исходниках. Которые разбиты на 100500 файлов, и алгоритм размазан по нескольким десяткам из них. Вот так смотришь, что куда передаётся — а там интерфейсы поверх интерфейсов, и тонны абстрактных фабрик фабрик, за которыми понять, как и где конкретно происходит собственно расчёт, не так-то просто. Создаётся впечатление, что код на 99% состоит из «воздуха», который реально ни во что не компилируется.
Создаётся впечатление, что код на 99% состоит из «воздуха», который реально ни во что не компилируется.
Такой же точно калькулятор, написанный в девяностых, был бы раз в сто меньше, как по объему бинарника, так и по потребляемой памяти.
У меня впечатление что этот код не только компилируется, но еще и пару миллионов пустых циклов добавляет.
Калькулятор Win XP мог посчитать 250000! (по логике — суммирование логарифмов с достаточной точностью). Но функции типа a^b считал до куда меньшего предела.
А вот в Win 7 уже не воспринимает результаты размером 1010000 и более.
Создаётся впечатление, что код на 99% состоит из «воздуха», который реально ни во что не компилируется.
Вы только что описали ДНК?
Думаю у этого калькулятора и внутри ассемблерного кода фабрика фабрик с воздухом внутри.
ну почему сразу «какая-то магия», просто переписал калькулятор с нуля и готово :D
«31 июля + 4 месяца = 30 ноября»
до исправления было лучше — сразу видно что ошибка.
а щас никто не заметит ошибку и будет использовать неправильный результат :(

Математически операция складывания месяцев с конкретным числом — это бред. Без уточнений по крайне менее. Мы можем взять месяц как стандартные 30 дней (что является математическим округлением среднего ~30.44), тогда, например, мы будем "пропускать" февраль: 31 января + 1 месяц = 1~2 марта. Можно "обрезать" месяц, тогда операция теряет ассоциативность: (31 января + 1 месяц) + 1 месяц != 31 января + (1 месяц + 1 месяц). Можно "сохранять" число при обрезании, но тогда повляются "странные" элементы: 28</28> февраля, 28</29> февраля, 28</30> февраля, 28</31> февраля (да и стремление максимально узаконить такие операции — странная, так как есть же ещё разные календари со свойствами транзитивности — всё это учитывать… непонятно зачем).


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

"… Не факт, что это ошибка"
— такая же команда есть в языках программирования, например в 1С Предприятие.
Лень проверять, но думаю что 1С скажет что это точно ошибка :)

Лучше проверить. Я проверил на python — у него в timedelta нельзя определить именно месяц. А вычитание конкретных дат приводит к конкретному количеству дней.

Дата = Дата("20190731");
Дата = ДобавитьМесяц(Дата, 4); //30.11.2019 0:00:00
1C язык запросов:
ВЫБРАТЬ ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(2019, 7, 31), МЕСЯЦ, 4) КАК Поле1
Результат: 30.11.2019 0:00:00

Ну вот, теперь в 1С все проверили :)
> Математически операция складывания месяцев с конкретным числом — это бред

А «следующая зарплата через месяц после 30 января» — тоже бред?
С обычной человеческой точки зрения это нормально, 5 февраля + месяц = 5 марта, и даже неважно високосный год или нет.
Но с математической это действительно бред, т.к. понятие месяц = неизвестная величина (28, 29, 30, 31), и в этом случае можно прибавлять только кол-во дней или недель (часов, минут и т.д.), т.е. строго детерминированные величины.

А что с человеческой точки зрения будет 31 января + месяц?

Если с точки зрения «месяцев», то «31 января» стоит читать как «конец января», значит через месяц — это «конец февраля», ну и это равно «28 (или 29) февраля».
Если же с точки зрения «дней», то надо определить значение «месяц» в днях. Думаю для большинства месяц = 30 дней, что даёт нам 1-2 марта.
Не согласен.
Когда вы прибавляете к дате один месяц, то вы прибавляете следующий месяц, а не средний.
Потому, для 31 января длина следующего месяца не 30, а 28 (29) дней, и именно 28 (29) дней и следует прибавить. Получим 28 (29) февраля.
то вы прибавляете следующий месяц

Или же текущий. 1 июня + 1 месяц — я ожидаю 1-е июля, а не 2-е (в июле 31 день, в июне 30).
«Прибавить длину текущего месяца (будет то же число месяца, если оно есть в следующем месяце), но остановиться на последнем дне при переполнении»?
Вообще мне кажется логичным проинформировать пользователя. Например, надписью «результат был скорректирован» и явно описать, в чём проблема. Выдавать возможно неверный результат как будто он правильный — не лучшая идея.
Всё таки стоит исходить из того, что нет однозначно верного ответа, что такое «через месяц после 31 января». И так как калькулятор не может знать, что ожидает пользователь, любой ответ может оказаться неверным для задачи пользователя.

Может показаться странно, но я ни разу не слышал — через месяц после 30 января. Через месяц — да, но это подразумевает уже само по себе ± пара дней. Но вообще контекст в таких вещах плохо работает и часто уточняют — в конце контекст.следующий_месяц, и даже в этом случае 146% переспросят "то есть в январе?". А именно такая формулировка встречается разве что в анекдотах про математиков (и задачах по спортивному программированию).

Нет не бред, потому что используют не просто «месяц», а «календарный месяц». Что уже сложнее ибо привязанно к календарю. И да скорее всего 28.01 +1 месяц = 28.02 а [29-31].01 + 1 месяц = 01.03
В операциях с месяцами основная ошибка — это попытка «взвесить» месяц в днях, что по определению невозможно, да и в корне неверно. Чтобы избежать неоднозначности, этого делать не нужно. В этом плане, очень правильно эта математика реализована, например, в PostgreSQL. Чтобы понять логику прибавления месяцев, проще всего взять пример зарплаты. Если заплата выплачивается каждый месяц, то месяц не может быть пропущен по определению. Иными словами, при добавлении месяца к любой дате в январе должна быть дата в феврале, но никак не в марте. Отсюда правда вытекают такие порой неочевидные моменты, как например:
28/29/30/31 января + 1 месяц = 28 февраля
(дата + N месяцев) - N месяцев не всегда равно дата
(дата + 1 месяц) + 1 месяц не всегда равно дата + 2 месяца
и т.п.
В таком случае это очень плохая идея — называть такую операцию «сложением».

Потому что сложение коммутативно, ассоциативно и дистрибутивно (с умножением), и все слишком сильно к этому привыкли.

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

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

ОК, если ввести 2 типа time и timeinterval, то можно ввести операцию их сложения.

Тогда можно определить значения типа timeinterval: '1 second', '1 day', '1 week'.
Но нельзя определить значения '1 month', '1 year', '1 century'.

И снова все будет работать.
Почему нельзя определить месяц? Это единичный интервал, для которого год и столетие кратные. Выразить эти интервалы в днях нельзя, но это не мешает их определению.
Очень во многих языках операция конкатенации строк обозначается символом "+", что тоже ничего общего не имеет со сложением.
Начнём с того, что не всегда. Второй момент, мы оперируем с разными величинами — датами и интервалами. Последние, в довершение всего, ещё и могут быть выражены в величинах, однозначно не приводимых друг к другу. А как называть/обозначать эту операцию — дело десятое.
Не бред, но и не операция сложения, как в комментариях уже указали.
Математически операция складывания месяцев с конкретным числом — это бред. Без уточнений по крайне менее.

Не бред, а недостаточная проработка логики. Если уж введена сама возможность складывать месяцы, то при выполнении такой операции должны фоном проводиться проверки: заданное условие DD.MM.YYYY проверяется на високосность, месяц точки отсчёта, таким образом прибавка 3х месяцев учитывает календарный состав следующих за заданным трёх месяцев и система точно знает — сколько там на самом деле дней. Это не так сложно, календарь всегда доступен для обращения за актуальными данными.
Когда дело касается дат и времени, ожидаемо что правила математики перестают работать. Можно ещё вспомнить что к дате прибавить 1 год 1 месяц и 1 день не то же самое, что к той же дате прибавить 1 месяц 1 день 1 год.

Тут важно, для чего считать. Месяц на бытовом уровне может быть примерным, особенно если речь идет о десятилетия.


А если о платежах каких-то — то может быть важен каждый день.

Но это всегда может быть какой-то перевёрнутый бит каким-то высокоэнергетическим лучом от какого-то дружественного космического соседа.

Я конечно понимаю, что это шутка, но вероятность того, что из-за этого повредится какой-то бит в памяти компьютера крайне мала, не говоря о вероятности повреждения конкретного. Скорее всего произойдте просто синий экран и компьютер перезгарузится. Ну или в какой-нибудь картинке пиксель или блок пикселей неправильно отобразится.

В расчете на один бит вероятность очень мала, а если взять сервер с терабайтами памяти то вероятность далека не такая маленькая, поэтому и используют ECC память. Насчет синего экрана, то как раз процент занимаемой памяти процессами очень мал, относительно данных на таких огромных объемах, и скорее подпортятся данные, чем использующий их процесс.
Исследования, проведенные IBM в 1990-х годах, показывают, что компьютеры обычно испытывают около одной ошибки, вызванной космическим лучом, на 256 мегабайт оперативной памяти в месяц.
О, спасибо, годная статейка.
Была даже статья (не могу найти) с переводом доклада с конференции, где это использовалось как реально работающий метод взлома. Зарегистрировать кучу похожих на google доменов с отличием в 1 бит, и из миллиардов запросов некоторые попадают.
Лично видел 2 + 2 = 5. В прошлом веке на 286м процессоре шлейфом от флоповода перекрыло вентилятор.

Вентилятор на 286? Откуда?

Да, но он направлен наружу и в XT и в AT корпусах.
Точно помню кулер на процессоре, и рабочее место расчетника лимитов, на котором стоял писюк желтой сборки. А вот могли поставить чтото дороже 286/287 или нет — мой склероз сообщать отказывается.
даже если по мнению процессора 2 + 2 уже 5, сообщить об этом он не сможет, будет не в состоянии )))
Не совсем так. Есть атака RawHammer, есть ещё классная атака где в URL по одному биту меняют Bit-squatting. Ситуаций, когда меняется один бит очень много.
любопытно, а 29 февраля + 1год это будет 28е февраля?
Да что вы знаете об ошибках локализации калькулятора =) Вот мой калькулятор:
image
Я как-то делал через долю месяца + округление.
1. Калькулятор нужен что бы работать на человека, а не на машину. Поэтому угождать надо человеку и 5 мая + месяц должно быть 5 июня… В целом математическую операцию +месяц в программировании можно свести к: берем число месяца, прибавляем 1, вставляем обратно.
2. В разнице между дат в программах всегда надо учитывать что именно за даты, какого года. Дальше уже понятно что +30 дней это +30 дней, а плюс месяц это плюс месяц…

В целом очень молодец.
Поэтому угождать надо человеку и 5 мая + месяц должно быть 5 июня…

А чему в таком случае будет равно «31 января + 1 месяц»?

31 февраля, очевидно.
И срабатывает валидация результата, которая говорит, что как-бы нет такой даты.
Тогда должна действовать поправка: если такого числа в новом месяце нет, берем раннее ближайшее существующее. Кстати, с +год для високосных и февраля то же нужно.
Выше уже обсудили, что как правило, 31 января + 1 месяц = 28/29 февраля, в зависимости от года.
Куда интереснее, чему должно быть равно (31 января + 1 месяц) + 1 месяц. Нужно ли помнить предысторию получения текущего значения даты.
(31 января + 1 месяц) + 1 месяц = 28/29 марта
31 января + (1 месяц + 1 месяц) = 31 января + 2 месяца = 31 марта

Достаточно логично, как по мне. Ассоциативность придётся закопать, конечно, но у нас ведь тут особое сложение.
такая же логика в древнем FoxPro)
Да, пожалуй, хотелось бы видеть именно 31 марта, но если, например, плюсуем по месяцу в цикле, то при наивной реализации получим 28 марта. Я пытался обратить внимание на это
И да, я согласен, что вешать такое поведение на стандартный оператор сложения — готовить почву для багов
А 30 января + 1 месяц = 28/29 февраля или 27/28 февраля?
если так идти назад по 1 дню, то 3 января + 1 месяц = 1 февраля?
Я себе это так представляю:

31 января + 1 месяц = 28/29 февраля
30 января + 1 месяц = 28/29 февраля
...
28 января + 1 месяц = 28 февраля
27 января + 1 месяц = 27 февраля


Это более-менее соответствует моему повседневному опыту работы с датами и хорошо формализуется. Но, повторюсь, сложением в обычном математическом смысле это не является. А пытаться натянуть даты на обычное сложение — плохая затея, получится только хуже.
По вашей логике 15 февраля + 1 месяц = 15 марта
Но 15 февраля — 2я половина месяца, а 15 марта — 1я половина месяца! Выходит что мы прыгнули меньше чем на 1 месяц!
Должно быть 15 февраля + 1 месяц = 16/17 марта
Вторая/первая половина месяца — как бухгалтерия скажет, так оно и будет. Если 15.01 сказали что следующая з/п будет через месяц, то хоть ты тресни, но она будет 15.02, и доводы что это вторая половина месяца никто слушать не будет. Вас же не смущает что в третьей декаде месяца от 8 до 11 дней.
Если вам 15 февраля скажут «давай через месяц», в вашей голове возникнет 15 марта или 16/17 марта? Если второе, то нам не о чем спорить, поскольку у нас разная интуиция касательно расчетов с датами. А если первое, то ещё раз повторю, что во варианте, которого я придерживаюсь, не используется обычное арифметическое сложение, соответственно неверно находить ошибки в нём, придерживаясь правил обычной арифметики.
Представьте, что у вас первая зарплата в компании 15 февраля. Так же рассуждать будете? ;) Сдвиг на месяц не предполагает привяки к половинам и каким-либо другим частям месяца — это всегда либо то же число, что и в прошлом месяце, либо максимальное дата месяца, если число в месяце отсутствует.
Выше уже обсудили

Ага, вижу. Я просто задал вопрос, когда обсуждения ещё не было.


как правило, 31 января + 1 месяц = 28/29 февраля, в зависимости от года.
Куда интереснее, чему должно быть равно (31 января + 1 месяц) + 1 месяц. Нужно ли помнить предысторию получения текущего значения даты.

К этому я и вёл. :) То, что прибавить 2 месяца и два раза прибавить месяц — это разные вещи, может привести к трудноуловимым багам. Если уж делать так, то не называть операцию сложением.

В старом калькуляторе из Windows 7 есть аналогичный баг, но выдается немного иной ответ
Изображение
image
Ох уж эти вечные проблемы с годом в 365,2425 дней. Когда я работал с климатическими моделями мы просто использовали 360-дневный год. 12 месяцев по 30 дней и всё. Красота!
UFO just landed and posted this here
Ну погоду то считают метеорологические модели, а не климатические. У них нормальный земной календарь.

А почему некто не догадался представить мечюсяцы как замкнутый двухсторонний цикл?
И прибавление месяца, это просто переход на новое значение в списке…
С годом тоде самое, но список не замкнутый
Пример 1:
10 февраля 2018


  • 5 месяцев
    должно работать вот как:
    Сохраняем в буфер количество дней
    Передвигаем замкнутый список месяцев на 5 позиций возвращаем дни.

Пример 2:
10 февраля 2018


  • 5 месяцев 25дней
    Повторяем все что в примере 1, а дальше
    Сохраняем сумму дней в переменную
    После чего
    Вытягиваем из двух связного списка количество дней в получившимся месяце
    И вычитаем из получившегося дней, если разность больше 0 переключаем месяц и добавляем разность, если меньше просто добавляем сумму дней

Вроде всё логично кроме одного понятия: сначала прибавлять дни, а уже потом месяцы или наоборот?
Лучший вариант-сделать галочку, что переключает это состояние

UFO just landed and posted this here

Уххх, а еще есть боль от номеров недель. "Надо сравнить продажи за 2 года с группировкой по номерам недель. ....wtf!!! почему один год у тебя начинается с последних чисел декабря предыдущего, а другой не с первого января????" :)

UFO just landed and posted this here
Предположим, что неделя должна начинаться с понедельника. Тогда если год начинается посреди недели, то первая неделя года начнётся в прошлом году. То же самое с месяцами. Первая неделя месяца начинается в прошлом месяце, отсюда и проблемы.
UFO just landed and posted this here
Не совсем. «Бизнесу» часто нужно сравнить продажи по производственным неделям, но в рамках календарного года. И вот тут начинается самое интересное.
UFO just landed and posted this here
Ну он ставит задачу, типа, «Сравнить показатели первой недели месяца с предыдущим»
UFO just landed and posted this here
При суммировании финансовых результатов всех недель месяца и всех дней месяца получатся разные цифры. Бизнес может выдвинуть претензии, что ваша программа глючная.
UFO just landed and posted this here

Вы слишком категоричны. Данная закавыка может возникнуть в любой конторе, в любой стране и никто не говорит, что она не решаема. Я говорю, что она решается через некоторую боль и страдания.
И разработчик все же должен включать мозги, не имплементировать тупо по "наиподробнейшему" ТЗ. Иначе это мартышка, а не девелопер

UFO just landed and posted this here
Очень точная характеристика ситуации, как я мог забыть эту эпохальную вещь :) прям в точку!
Первая неделя года — это неделя, которая включает в себя 4 января (в странах где неделя начинается с понедельника).
У нас в стране есть такое понятие как «производственный календарь»
В его контексте, бывают не только недели которые начинаются с прошлого года, но даже года с разным кол-вом этих самых недель.
Эта тема не столько однозначная как может показаться на первый взгляд.
У меня на семерке работает без аномалий — 5 месяцев и 6 дней. Видимо у автора виндовс 10.
image
Оффтоп не по теме
Эх, прекрасное стекло в интерфейсе. Как же я тоскую по нему…

Но ведь это неверно, результат меньше 5 месяцев

По идее между 31.07 и 30.12 не должно быть больше 5 месяцев
Если по человечески подумать, то 31.07 + 5 месяцем, это 31.12,
а тут 30.12. По мне это 5 месяцев без одного дня,
а не 5 месяцев и 6 дней
Кроме того, 152 дня никак не соответствуют 5 месяцам и 6 дням. В месяце получается 29,2 дня в таком случае.
Похоже от 30-г декабря до 31-го -6 дней
image
Как раз 5 месяцев 6 дней — аномалия.
31.07.19 + 5m6d = 06.01.20
30.12.19 - 5m6d = 24.07.19
Да у калькулятора и перевод на русский знатный: From — От, To — КОМУ.
КОМУ, Карл! :)
И до сих пор никто не вспомнил про таймзоны, летнее время, советский революционный календарь и особенности счёта времени при движении вокруг света. А кто-то даже до сих пор настолько наивен, что недели, дни, часы и минуты считает точно определёнными и неизменными величинами.
В Java:
var first  = LocalDate.of(2019, JULY, 31);
var second = LocalDate.of(2019, DECEMBER, 30);

print(Period.between(first, second));


Выведет P4M30D, что очень близко с результатом после починки (т.к. P30D равен P4W2D). Может это и «неправильно» — но неправильно скорее думать об этой операции как о каноничном сложении. Зато теперь калькулятор выдаёт тот же ответ, что многие другие приложения (в частности, написанные на Java, да).

Смотрите, похоже, с годами тоже аналогичная проблема проявляется


28.02.2016


Тогда от 28 февраля 2016 года до 29 февраля 2016 года всего 0 дней, таким образом мы доказали, что 28 и 29 февраля 2016 года — один и тот же день.
Я выше уже писал, что прибавить один год затем один день это не то же самое, что прибавить день, а затем прибавить один год. При прибавлении целого года никто не ожидает что поменяется месяц, а с днями — легко.
Sign up to leave a comment.

Articles