Ошибки в программе калькулятор
Эта статья — продолжение истории про калькулятор на JavaScript. В предыдущих частях мы:
Теперь наш калькулятор будут смотреть инженеры по тестированию — постараются найти в нём ошибки логики, поведения, вычислений и прочие неявные моменты.
👉 В этой статье не будет автотестов, юнит-тестов, API-тестов и интеграционных тестов. Это всё нужные инструменты, но задача тестировщика, в частности, — выбрать правильный инструмент тестирования.
Если у него в работе очень простая программа (как наш калькулятор), то нет смысла городить автоматизацию и делать тесты ради тестов. Иногда можно и вручную всё проверить и получить точно такой же результат.
Базовое тестирование
Первое, что мы делаем, — проверяем, а как вообще ведут себя кнопки математических действий и делают ли они то, что нужно. Возьмём два числа — 12 и 5 — и сравним результаты всех действий с тем, что даёт калькулятор:
Отрицательные числа тоже отображаются и считаются правильно.
Тестируем большие числа
У компьютеров есть нюанс: любые переменные имеют ограничения по размеру числа. Например, если на переменную выделено 16 бит, то максимальное число, которое можно в нее положить, — 65 536. Число на единицу больше уже потребует 17 бит, а мы столько не выделяли.
Мы хоть и разработчики этого калькулятора, но мы не помним, какое число имели в виду, когда заводили переменную. Может быть, это решение за нас принял JavaScript. Поэтому нужно проверить, не сломается ли наш калькулятор от больших чисел.
Пробуем: 123 456 789 × 2 = 246 913 578 — верно
А вот необычный эксперимент:
12 345 678 901 234 567 × 1 = 12 345 678 901 234 568
Ух ты! Мы умножили большое число на единицу, а в ответе появилась ошибка. Это значит, что настолько длинные числа за раз наш калькулятор уже обработать не в состоянии.
❌ Неправильно обрабатываются 17-значные числа и те, которые больше них.
А если мы попробуем получить 17-значное число в ответе, интересно, оно тоже будет с ошибкой?
Да, в ответе тоже неверное число — 8 × 4 = 32, поэтому в конце должно стоять 2, а не 0. Пишем баг:
❌ Если в ответе получается 17-значное число или более — ответ точно неверный.
При этом деление на 16-значное число работает верно:
Тестирование математических трюков
Теперь попробуем разделить на ноль:
Отказоустойчивость
А что если оставить поле ввода пустым и попробовать что-то посчитать? Давайте посмотрим:
Скрипт преобразовал пустую строку в ноль и получил ответ, но это неправильно — при отсутствии одного из чисел калькулятор должен сообщить об этом, а не продолжать считать.
Пойдём дальше и введём слово вместо числа:
Скрипт честно пытается перевести строку в число, у него это не получается, поэтому он выдаёт неопределённое значение.
❌ Нет проверки на то, ввели число или строку.
И напоследок проверим что будет, если мы что-то введём, но не выберем ни одно действие:
Тоже плохо. Надо будет обработать такую ситуацию.
❌ Нет проверки, когда не выбрали ничего из математических действий.
Что делаем
После тестирования у нас получился такой список ошибок:
❌ Неправильно обрабатываются 17-значные числа и те, которые больше них.
❌ Если в ответе получается 17-значное число или более — ответ точно неверный.
❌ Нет проверки на то, ввели число или строку.
❌ Нет проверки, когда не выбрали ничего из математических действий.
Проверяем, что ввели число, а не слова или другие символы
Для перевода строку в число в JavaScript используют функцию Number() . Если строку можно перевести в число — она сработает без ошибок, а если не получится — вернёт значение NaN. Это значит, что числа не получилось.Чтобы проверить, прошло всё нормально или нет, мы будем использовать функцию isNaN() — она сравнит значение переменной с NaN, и вернёт true, если в переменной лежит NaN. А чтобы не путать числа и строки для сравнения, переименуем переменные в самом начале скрипта и сделаем их принудительно строками:
Проверяем, что нет пустых значений
Ещё надо дополнительно добавить проверку на пробелы — JavaScript строку из пробелов тоже переводит как ноль, а нам это не нужно:
Обрабатываем деление на ноль
Простая проверка — добавляем сравнение второго числа с нулём:
Обрабатываем длинные числа
Даже если мы ограничим каждое поле ввода числами по 16 знаков вместо 17, то при перемножении они дадут нам в ответе 32 знака — а это тоже превышает наш предел точности. Чтобы гарантированно получить в ответе число не больше 16 разрядов перед запятой, нам нужно, чтобы оба числа были не больше 99 999 999 — в нём 8 разрядов, а при перемножении мы получим максимум 16, как раз то, что нужно.
Чтобы это сделать, добавим проверку на размер числа:
Если не выбрано математическое действие
С этим всё просто — добавляем в case действие по умолчанию, которое выполнится, если никакие из вариантов не подойдут:
В итоге
✅ Калькулятор не работает с числами больше 16 знаков до запятой и предупреждает об этом пользователя
На днях компания Microsoft открыла исходный код калькулятора. Это приложение входило во все версии операционной системы Windows. Исходный код разных проектов Microsoft достаточно часто открывался за последние годы, но новость о калькуляторе в первый же день просочилась даже в нетехнологические средства массовой информации. Что ж, это популярная, но очень маленькая программа на языке C++. Тем не менее, статический анализ кода с помощью PVS-Studio выявил подозрительные места в проекте.
Введение
Калькулятор Windows наверняка знаком каждому пользователю этой операционной системы и не требует особого представления. Теперь же любой пользователь может изучить исходный код калькулятора на GitHub и предложить свои улучшения.
Общественность, например, уже обратила внимание на такую функцию:
которая логирует текст из буфера обмена и, возможно, отправляет его на серверы Microsoft. Но эта заметка не об этом. Хотя подозрительных примеров кода будет много.
Про неправильное сравнение строк
V547 Expression 'm_resolvedName == L«en-US»' is always false. To compare strings you should use wcscmp() function. Calculator LocalizationSettings.h 180
Я просматриваю отчёты анализатора, сортируя их по возрастанию номеров диагностик, и предупреждение на этот код было самым первым в списке, и очень удачным.
Дело в том, что здесь неправильно сравниваются строки. Получилось сравнение указателей вместо значений строк. Сравнивается адрес массива символов с адресом строкового литерала. Указатели всегда неравны, поэтому условие всегда ложно. Для правильного сравнения строк следует использовать, например, функцию wcscmp.
Кстати, пока я пишу эту статью, в заголовочном файле массив символов m_resolvedName превратился в полноценную строку типа std::wstring. И теперь сравнение работает правильно. К моменту, когда вы будете читать эту статью, скорее всего, многие другие ошибки тоже будут исправлены благодаря энтузиастам и таким исследованиям, как это.
Утечка памяти в нативном коде
V773 The function was exited without releasing the 'temp' pointer. A memory leak is possible. CalcViewModel StandardCalculatorViewModel.cpp 529
Мы видим указатель temp, ссылающийся на массив из 100 элементов, под который выделена динамическая память. К сожалению, память освобождается всего в одном месте функции, во всех остальных местах возникает утечка памяти. Она не очень большая, но это всё равно ошибка для C++ кода.
Неуловимое исключение
V702 Classes should always be derived from std::exception (and alike) as 'public' (no keyword was specified, so compiler defaults it to 'private'). CalcManager CalcException.h 4
Анализатор обнаружил класс, унаследованный от класса std::exception через модификатор private (модификатор по умолчанию, если ничего не указано). Проблема такого кода заключается в том, что при попытке поймать общее исключение std::exception исключение типа CalcException будет пропущено. Такое поведение возникает потому, что приватное наследование исключает неявное преобразование типов.
Пропущенный день
V719 The switch statement does not cover all values of the 'DateUnit' enum: Day. CalcViewModel DateCalculator.cpp 279
Подозрительно, что в switch не рассмотрен случай с DateUnit::Day. Из-за этого в календарь (переменная m_calendar) не добавляется значение, связанное с днём, хотя метод AddDays у календаря присутствует.
- V719 The switch statement does not cover all values of the 'eANGLE_TYPE' enum: ANGLE_RAD. CalcManager trans.cpp 109
- V719 The switch statement does not cover all values of the 'eANGLE_TYPE' enum: ANGLE_RAD. CalcManager trans.cpp 204
- V719 The switch statement does not cover all values of the 'eANGLE_TYPE' enum: ANGLE_RAD. CalcManager trans.cpp 276
Подозрительные сравнение вещественных чисел
V550 An odd precise comparison: ratio == threshold. It's probably better to use a comparison with defined precision: fabs(A — B) < Epsilon. Calculator AspectRatioTrigger.cpp 80
Анализатор указал на подозрительное выражение ratio == threshold. Эти переменные типа double и точное сравнение таких переменных простым оператором равенства вряд ли возможно. Тем более, что значение переменной ratio получено после операции деления.
- V550 An odd precise comparison. It's probably better to use a comparison with defined precision: fabs(A — B) < Epsilon. CalcManager UnitConverter.cpp 752
- V550 An odd precise comparison: stod(roundedString) != 0.0. It's probably better to use a comparison with defined precision: fabs(A — B) > Epsilon. CalcManager UnitConverter.cpp 778
- V550 An odd precise comparison. It's probably better to use a comparison with defined precision: fabs(A — B) < Epsilon. CalcManager UnitConverter.cpp 790
- V550 An odd precise comparison: stod(roundedString) != 0.0. It's probably better to use a comparison with defined precision: fabs(A — B) > Epsilon. CalcManager UnitConverter.cpp 820
- V550 An odd precise comparison: conversionTable[m_toType].ratio == 1.0. It's probably better to use a comparison with defined precision: fabs(A — B) < Epsilon. CalcManager UnitConverter.cpp 980
- V550 An odd precise comparison: conversionTable[m_toType].offset == 0.0. It's probably better to use a comparison with defined precision: fabs(A — B) < Epsilon. CalcManager UnitConverter.cpp 980
- V550 An odd precise comparison: returnValue != 0. It's probably better to use a comparison with defined precision: fabs(A — B) > Epsilon. CalcManager UnitConverter.cpp 1000
- V550 An odd precise comparison: sizeToUse != 0.0. It's probably better to use a comparison with defined precision: fabs(A — B) > Epsilon. CalcViewModel LocalizationService.cpp 270
- V550 An odd precise comparison: sizeToUse != 0.0. It's probably better to use a comparison with defined precision: fabs(A — B) > Epsilon. CalcViewModel LocalizationService.cpp 289
- V550 An odd precise comparison: sizeToUse != 0.0. It's probably better to use a comparison with defined precision: fabs(A — B) > Epsilon. CalcViewModel LocalizationService.cpp 308
- V550 An odd precise comparison: sizeToUse != 0.0. It's probably better to use a comparison with defined precision: fabs(A — B) > Epsilon. CalcViewModel LocalizationService.cpp 327
- V550 An odd precise comparison: stod(stringToLocalize) == 0. It's probably better to use a comparison with defined precision: fabs(A — B) < Epsilon. CalcViewModel UnitConverterViewModel.cpp 388
Подозрительная последовательность функций
V1020 The function exited without calling the 'TraceLogger::GetInstance().LogNewWindowCreationEnd' function. Check lines: 396, 375. Calculator App.xaml.cpp 396
Диагностика V1020 анализирует блоки кода и эвристически пытается найти ветви, в которых забыт вызов функции.
В приведённом выше фрагменте кода анализатор нашёл блок с вызовом функций LogNewWindowCreationBegin и LogNewWindowCreationEnd. Ниже он нашёл похожий блок кода, в котором функция LogNewWindowCreationEnd вызывается только при определённых условиях, что подозрительно.
Ненадёжные тесты
V621 Consider inspecting the 'for' operator. It's possible that the loop will be executed incorrectly or won't be executed at all. CalculatorUnitTests UnitConverterViewModelUnitTests.cpp 500
Анализатор обнаружил цикл for, в котором не выполняется ни одна итерация, а, следовательно, не выполняются и тесты. Начальное значение счётчика цикла button (93) сразу превышает конечное (0).
V760 Two identical blocks of text were found. The second block begins from line 688. CalculatorUnitTests UnitConverterViewModelUnitTests.cpp 683
Ещё один тест с подозрительным кодом. Анализатор обнаружил идентичные фрагменты кода, выполняющиеся друг за другом. Возможно, код писался копированием этих фрагментов, но программист забыл изменить часть кода.
V601 The 'false' value is implicitly cast to the integer type. Inspect the second argument. CalculatorUnitTests CalcInputTest.cpp 352
В функцию ToRational передают булевское значение false, хотя параметр имеет тип int32_t и называется precision.
Я решил отследить используемое значение в коде. Далее оно передаётся в функцию StringToRat:
а затем в StringToNumber:
И вот тело нужной функции:
Тут мы видим, что переменная precision стала называться starting и участвует в выражении cdigits > starting, что очень странно, ведь туда изначально передали значение false.
Избыточность
V560 A part of conditional expression is always true: NumbersAndOperatorsEnum::None != op. CalcViewModel UnitConverterViewModel.cpp 991
Переменная op уже сравнивалась со значением NumbersAndOperatorsEnum::None и дублирующую проверку можно удалить.
V728 An excessive check can be simplified. The '(A && B) || (!A && !B)' expression is equivalent to the 'bool(A) == bool(B)' expression. Calculator Calculator.xaml.cpp 239
Это гигантское условное выражение изначально имело ширину 218 символов, но я разбил его на несколько строк для демонстрации предупреждения. А переписать код можно до такого короткого и, главное, читабельного варианта:
V524 It is odd that the body of 'ConvertBack' function is fully equivalent to the body of 'Convert' function. Calculator BooleanNegationConverter.cpp 24
Анализатор обнаружил две функции, которые реализованы одинаково. По названиям функций Convert и ConvertBack можно предположить, что они должны выполнять разные действия, но разработчикам виднее.
Заключение
Наверное, каждый открытый проект от Microsoft давал нам возможность показать важность применения методологии статического анализа. Даже на таких маленьких проектах, как калькулятор. В таких крупных компаниях, как Microsoft, Google, Amazon и других, работает много талантливых программистов, но они такие же люди, которые делают ошибки в коде. Применение инструментов статического анализа кода — один из хороших способов повысить качество программ в любых командах разработчиков.
Проверь свой «Калькулятор», скачав PVS-Studio и попробовав на своём проекте. :-)
Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Svyatoslav Razmyslov. Counting Bugs in Windows Calculator
Сколько ошибок в программе? Это вопрос, который волнует каждого программиста. Особую актуальность придает ему принцип кучкования ошибок, согласно которому нахождение в некотором модуле ошибки увеличивает вероятность того, что в этом модуле есть и другие ошибки. Точного ответа на вопрос о количестве ошибок в программе очень часто дать невозможно, а вот построить некоторую оценку — можно. Для этого существуют несколько статических моделей. Рассмотрим одну из них: Модель Миллса.
В 1972 г. суперпрограммист фирмы IBM Харлан Миллс предложил следущий способ оценки количества ошибок в программе. Пусть у нас есть программа. Предположим, что в ней N ошибок. Назовем их естественными. Внесем в нее дополнительно M искусственных ошибок. Проведём тестирование программы. Пусть в ходе тестирования было обнаружено n естественных ошибок и m искусственных. Предположим, что вероятность обнаружения для естественных и искусственных ошибок одинакова. Тогда выполняется соотношение:
Мы нашли один и тот же процент естественных и искусственных ошибок. Отсюда количество ошибок в программе:
Количество необнаруженных ошибок равно (N-n).
Например, пусть в программу внесено 20 искусственных ошибок, в ходе тестирования было обнаружено 12 искусственных и 7 естественных ошибок. Получим следущую оценку количества ошибок в программе:
Количество необнаруженных ошибок равно (N-n) = 12 — 7 = 5.
Легко заметить, что в описанном выше способе Миллса есть один существенный недостаток. Если мы найдем 100% искусственных ошибок, это будут означать, что и естественных ошибок мы нашли 100%. Но чем меньше мы внесем искусственных ошибок, тем больше вероятность того, что мы найдём их все. Внесем единственную исскуственную ошибку, найдем ее, и на этом основании объявим, что нашли все естесственные ошибки! Для решение такой проблемы Миллс добавил вторую часть модели, предназначенную для проверки гипотезы о величине N:
Предположим, что в программе N естественных ошибок. Внесём в неё M искусственных ошибок. Будем тестировать программу до тех пор, пока не найдем все искусственные ошибки. Пусть к этому моменту найдено n естественных ошибок. На основании этих чисел вычислим величину C:
Величина C выражает меру доверия к модели. Это вероятность того, что модель будет правильно отклонять ложное предположение. Например, пусть мы считаем, что естественных ошибок в программе нет (N=0). Внесем в программу 4 искусственные ошибки. Будем тестировать программу, пока не обнаружим все искусственные ошибки. Пусть при это мы не обнаружим ни одной естественной ошибки. В этом случае мера доверия нашему предположению (об отсутствии ошибок в программе) будет равна 80% (4 / (4+0+1)). Для того чтобы довести ее до 90% количество искусственных ошибок придется поднять до 9. Следущие 5% уверенности в отсутствии естественных ошибок обойдутся нам в 10 дополнительных искусственных ошибок. M придется довести до 19.
Если мы предположим, что в программе не более 3-х естественных ошибок (N=3), внесем в нее 6 искусственных (M=6), найдем все искусственные и одну, две или три (но не больше!) естественных, то мера доверия к модели будет 60% (6 / (6+3+1)).
Значения функции С для различных значений N и M, в процентах:
Таблица 1 — с шагом 1;
Таблица 2 — с шагом 5;
Из формул для вычисления меры доверия легко получить формулу для вычисления количества искусственных ошибок, которые необходимо внести в программу для получения нужной уверенности в полученной оценке:
Количество исскуственных ошибок, которые необходимо внести в программу, для достижения нужной меры доверия, для различных значений N:
Таблица 3 — с шагом 1;
Таблица 4 — с шагом 5;
Модель Миллса достаточно проста. Ее слабое место — предположение о равновероятности нахождения ошибок. Чтобы это предположение оправдалось, процедура внесения искусственных ошибок должна обладать определенной степенью «интеллекта». Ещё одно слабое место — это требование второй части миллсовой модели отыскать непременно все искусственные ошибки. А этого может не произойти долго, может быть, и никогда.
Здравствуйте! В этой публикации я хочу рассказать Вам, как сделать продвинутый калькулятор на Python 3 с Tkinter.
Итак, импортируем модули, которые нам понадобятся для нашей программы.
Библиотека tkinter нам необходима для создания окна, грубо говоря.
Модуль math нам нужен для математических операций.
Следующими двумя строками мы создаем окно и даем ему имя.
Создаем список с именами будущих кнопок калькулятора. Я выбрал все самые интересные функции, чтобы продемонстрировать, как их реализовать.
Следующим отрезком кода мы создаем кнопки для нашего калькулятора.
В каждом калькуляторе есть, так называемое поле ввода, в которое пользователь вводит нужные данные для программы. Это могут быть цифры, функции и математические операции. Их можно вводить, как с клавиатуры, так и при нажатии на кнопку в калькуляторе.
Пример 1. Я нажимаю на кнопку «2» в калькуляторе и в этом поле ввода, отображается цифра 2.
В Python Tkinter поле ввода называется Entry, а, например, в Java Script — input.
Мы подошли к основной задаче калькулятора — его функциям и логике.
До этого момента нами было создан внешний вид программы. Если бы Вы попробовали запустить ее и нажать на кнопку, Вам бы выскочила ошибка, ведь у нас вовсе нет функций калькулятора.
Приступим, пропишем нашему калькулятору логику и способность считать.
str1 = "-+0123456789.*/)("
Этой кода мы разрешаем пользователю вводить только символы -+0123456789.*/)(, а остальные исключаем, запрещаем вводить.
В этом блоке кода мы используем функцию eval — это, если можно так сказать, компилятор внутри компилятора. Она будет считать в нашей программе.
Создаем функцию изменения минуса на плюс.
Пример 2. Мы ввели в окно Entry 5, при нажатии на кнопку "±", калькулятор выведет -5.
И наоборот, мы ввели -5, нажали на кнопку "±" и получили ответ от программы 5.
Следующая функция — число pi. При нажатии на кнопку «П» программа выведет нам 3.14159265359, то есть число Pi. Вот тут нам и пригодилась библиотека math.
Функция выхода из программы. При нажатии на кнопку «Exit» окно Tkinter будет уничтожено и процесс остановлен. В этой функции нам нужна была библиотека sys.
Функция возведения в степень. Нужно ввести число, которое нужно возвести в степень. Далее программа выводит **. В Python этот символ означает возведение в степень 2**6 (возведение 2 в степень 6). Мы используем для счета в программе eval, а значит можно выполнить это так же, как и в Питоне. Ну и в конце мы вводим необходимую степень.
Пример 3. Нам нужно 3 возвести в 5 степень. Вводим число 3, нажимаем на кнопку «xⁿ» (3**. ) и вводим необходимую степень, — 5 (3**5). Нажимаем на кнопку " python">elif key == "xⁿ": calc_entry.insert(END, "**")
Опишу сразу две функции, так, как они идентичны.
Функция sin x и cos x.
Все просто, при нажатии на клавишу sin или же cos мы получаем синус или косинус по данному числу.
Функция получения факториала из данного числа.
Большое спасибо за прочтение данной публикации. Надеюсь она Вам была полезна.
После установки различных программ, игр и приложений в операционной системе Windows 7 некоторые пользователи сталкиваются с проблемой. Заключается она в некорректной работе одной из стандартных программ – калькулятором.
Проблема с калькулятором в Windows 7 заключается в том, что поле отображения вводимых цифр имеет половинный размер и каждое вновь вводимое значение наслаивается на предыдущее. В таком случае считать полученные данные чрезвычайно сложно, а порой и невозможно.
Ошибка стандартного калькулятора в Windows 7
Данный глюк калькулятора в Windows 7, как правило, появляется после установки различных приложений и программ, таких как игры, например. Особенно это имеет место, если программы, мягко говоря "взломанные", то есть нелицензионные.
Несмотря на то, что стандартный калькулятор нужен не всегда и необходимость им воспользоваться возникает довольно редко, всё же огорчает тот факт, что его в необходимых случаях нет под рукой.
Поэтому рассмотрим процесс, с помощью которого можно устранить глюк калькулятора в Windows 7.
Причиной возникновения "урезанного" окна в калькуляторе является замена стандартных шрифтов на другие при установке какой-либо программы. Именно из-за этого и происходит неправильное отображение окна ввода и путаница цифр.
Для того чтобы устранить проблему, необходимо восстановить первоначальные значения в реестре операционной системы.
Процедура эта совсем не сложная, но довольно ответственная. Как известно, в реестре хранятся настройки операционной системы, и неправильное редактирование значений реестра может обернуться её неработоспособностью. Поэтому производить все операции необходимо аккуратно и точно .
Итак, для редактирования реестра в Windows 7 нужно запустить редактор реестра. Делается это также как и в Windows XP:
«Пуск» – «Все программы» – «Стандартные» – «Выполнить». Появиться окно «Выполнить».
Стандартная программа «Выполнить»
В него вводим название программы, которую хотим запустить – regedit.exe – жмём «ОК».
Запуск «Редактора реестра»
Появиться окно программы «Редактор реестра».
Окно «Редактора реестра»
Далее щёлкаем на стрелочки рядом с папкой. При щелчке будут показаны вложенные папки. Таким образом, следуем по пути:
«HKEY_LOCAL_MACHINE» – «SOFTWARE» – «Microsoft» – «Windows NT» – «CurrentVersion». Далее находим папку «FontSubstitutes» и кликаем по ней. В правой части окна будут показаны параметры и значения. Их то мы и будем редактировать.
В правой части окна в алфавитном порядке будут показаны параметры. В колонке "Значение" будут указаны присвоенные шрифты. В колонке "Имя" находим MS Shell Dlg,204.
Внимание ! Надо найти именно этот пункт! Здесь легко допустить ошибку, так как рядом будут другие параметры с похожим названием (MS Shell Dlg; MS Shell Dlg 2; MS Shell Dlg,0). Изменять значение необходимо у параметра MS Shell Dlg,204 иначе результата не будет.
Параметр реестра "MS Shell Dlg,204"
Изменяем значение с MS Sans Serif,204 на Microsoft Sans Serif,204. Для этого щёлкаем правой кнопкой мыши на MS Shell Dlg,204 и выбираем пункт «Изменить…». Откроется окно «Изменение строкового параметра». В поле «Значение» вводим Microsoft Sans Serif,204. Изменяем MS на Microsoft.
Изменение строкового параметра
Для того чтобы изменения, внесённые в реестр вступили в силу – перезагружаем компьютер.
Затем проверяем, пропал ли глюк калькулятора. Если всё сделано верно, то, как правило, проблема устраняется, и калькулятор восстанавливает свою прежнюю работоспособность.
Читайте также: