Задать вопрос
  • Как в Linux через raw sockets подключиться к tcp-серверу, используя Qt?

    @Mercury13
    Программист на «си с крестами» и не только
    ::connect
    Ответ написан
    Комментировать
  • Как организована многопоточность в играх жанра RTS?

    @Mercury13
    Программист на «си с крестами» и не только
    Без параллельности можно обойтись — и поначалу лучше обойдитесь.
    Если уж хочется помногопоточить — я бы разбил на два потока, интеллект и графику.

    У каждого юнита три одинаковых структуры, renderInfo, bufferedInfo и aiInfo. Там могут быть координаты, курс, HP, ссылка на следующего — всё, что угодно. Ссылка на следующего — это важно, ведь юниты могут появляться и исчезать.

    Поток интеллекта работает с aiInfo; где-то здесь есть и код мультиплеера. Закончив шаг, поток захватывает мьютекс и для всех юнитов (понятие «все» определяется по aiInfo) даёт bufferedInfo = aiInfo.

    Поток прорисовки захватывает тот же мьютекс и для всех юнитов («все» по bufferedInfo) даёт renderInfo = bufferedInfo. А затем, освободив мьютекс, делает с этим renderInfo что хочет.
    Под мьютексом будем сидеть очень мало: ни прорисовки, ни мультиплеера там нет.

    Поскольку юнит исчезает из памяти только тогда, когда на него пропадают ссылки во всех трёх структурах, надо делать или объектный пул (т.е. юнит, будучи созданным, остаётся навсегда, но впоследствии этот блок памяти можно будет задействовать на другой юнит), или какие-нибудь «мусорные» указатели наподобие std::shared_ptr.

    Такая система аналогична тройной буферизации в прорисовке. Только в видяшной тройной буферизации асинхронно действуют кадры монитора и кадры рендерера; у нас — такты игры и кадры рендерера.

    Разбивать интеллект на потоки откровенно тяжело, и в игре с мультиплеером я просто не знаю, как. RTS обычно передают по сети короткие пакеты наподобие «выделить юнитов в квадрате (X,Y) — (X,Y)», «добавить к выделению юнита 1234» и «отправиться в точку (X, Y)». Эти команды, будучи выполненными на разных компьютерах, выполняются одинаково.

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

    Отдельный вопрос — компенсация пинга в играх по интернету. Не думал пока над этим. Наверно, придётся иметь officialAiInfo и lagCompensatedAiInfo…

    ЗЫ. Подсказывают, что такт игры в такой ситуёвине длится порядка 0,1 с, т.е. может продлиться несколько кадров. Тогда во всех этих …info надо описать движение так, чтобы в любой момент получить промежуточное x(t), y(t), yaw(t)… Для танка — bodyYaw(t) и turretYaw(t), для человечка — фазу анимации…

    ЗЗЫ. Если игра трёхмерная и вид хоть как-то настраивается, одна из команд игры будет выглядеть «выделить юнитов в квадрате (X,Y) — (X,Y) с матрицей преобразования A».

    ЗЗЗЫ. Чтобы игра была повторяемой (важно для мультиплеера), все расчёты, влияющие на течение игры, проводить в фиксированной запятой. Все переменные, с этим связанные, чем угодно, хоть нулём, но инициализировать.
    Ответ написан
    Комментировать
  • Какие есть книги про учёных для детей?

    @Mercury13
    Программист на «си с крестами» и не только
    Ирина (Ирэн) Радунская. Очень красиво писала, хоть и староваты книги.
    Ответ написан
    Комментировать
  • Преобразование дат в Delphi 7?

    @Mercury13
    Программист на «си с крестами» и не только
    Я давно не помню Delphi 7, но и там была версия StrToDate…
    function StrToDate ( const Date : string; const FormatSettings : TFormatSettings ) : TDateTime;

    Несколько форматов — несколько TFormatSettings.

    Далее. Выброс аварий — штука неэффективная и потому желательно, чтобы аварии вываливались действительно в авариях. Не знаю, есть ли в Delphi 7, но должна быть и такая функция.
    function TryStrToDate(const S: string; out Value: TDateTime; const AFormatSettings: TFormatSettings): Boolean;

    Она сигнализирует неудачу возвратом false.

    И наконец. Будь осторожен, в США принят формат даты через слэш, но MM/DD/YY. А ещё есть формат дат ISO, YYYY-MM-DD.
    Ответ написан
    Комментировать
  • Почему не компилируется helloworld с использованием wxWidgets?

    @Mercury13
    Программист на «си с крестами» и не только
    Project → Build Options → Search Directories → Linker
    Ответ написан
  • Что значит последовательный участок памяти?

    @Mercury13
    Программист на «си с крестами» и не только
    Один последовательный участок памяти — это значит «последовательные адреса памяти». Таковыми будут массив простой и динамический, строка традиционного устройства, и все структуры памяти, что на них основаны. В том числе простейший стек и кольцевая очередь.

    Для чего это нужно?
    1. Это ближе к железу: проще код, лучше работает кэш, а значит, быстрее.
    2. Некоторые функции, особенно нешаблонные, для своей работы требуют именно последовательных ячеек памяти.

    Но если я реализую стек через список, разве это не будет противоречить сказанному?

    Совершенно верно, будет. Отсюда и знаменитая ошибка «переполнение стека» — непонятно, как наладить стек вызовов бесконечной ёмкости и относительно простого устройства. Потому, если нужна очень глубокая рекурсия (например, при обходе сетей), стек вызовов приходится эмулировать.
    Ответ написан
    7 комментариев
  • Как открыть в мобильном браузере сайт на localhost?

    @Mercury13
    Программист на «си с крестами» и не только
    Если мобильное устройство не использует прокси-браузер (Opera Mini и прочие) и висит на той же радиоточке, что и стационарник — 1) настраиваем радиоточку так, чтобы с WiFi-клиентов был доступ в локальную сеть; 2) поднимаем на стационарном компьютере веб-сервер. Доступ — http://192.168.1.2:3000/site, внутренний IP-адрес компьютера можно узнать программой ipconfig; порт (3000) определяется настройками веб-сервера.

    Если либо используется прокси-браузер, либо смартфон в интернете через ОпСоСа, надо поднять веб-сервер и сделать, чтобы он был виден из интернета. Либо залить на хостинг, либо пробросить порт на маршрутизаторе. Доступ — http://12.34.56.78:3000/site, внешний IP-адрес можно узнать через 2ip.ru. Порт (3000) зависит от настройки перенаправления портов, и для безопасности его крайне нежелательно делать стандартными 80, 3128 и 8080.

    Как пробросить порт на маршрутизаторе — гуглите уж сами; состоит из двух вещей: как зафиксировать IP и как вбить в NAT новое правило перенаправления.
    Ответ написан
    Комментировать
  • Как заполняются многомерные массивы?

    @Mercury13
    Программист на «си с крестами» и не только
    New(Psh[4, 1]);
    Переводим на C#. Правда, тут есть одна загвоздка. Массивы в c# начинаются только с 0, и надо смотреть: либо заводить холостой 0-й элемент, либо корректировать индексы. Так что перед нами два варианта.
    Psh[4][1] = new short[393];
    Psh[3][0] = new short[392];
    (для примера работаю со вторым)

    function OpenA(FileName: ShortString; Mode: LongWord): integer;
    Stream OpenA(string FileName, FileAccess access);


    Как перевести ReadA? Это не так просто. Дело в том, что языки со стандартным промежуточным кодом (к ним относятся в первую очередь языки семейства Java и .NET) обычно пишут в порядке байтов Motorola. На Delphi файл писался в порядке Intel. Так что мы считываем 392 word’а, а затем смотрим, что делать: либо обернуть байты, либо сказать: теперь у нас новый стандарт — Motorola!

    if (!ReadA(Df[3][0], Psh[3][0], 392) {
      CloseA(Df[3, 0]);
      Psh[3][0] = null;
      return;
    }


    И последний рывок.
    Pkts[4, 1]:= @Psh[4, 1]^[9];
    Из-за отсутствия указателей (точнее, есть они, но мы же не хотим unsafe-кода) придётся Pkts разбивать на две части: ссылку на массив и смещение в массиве.
    Pkts[3][0].array = Psh[3][0];
    Pkts[3][0].offset = 8;
    Ответ написан
  • Как работает quicksort?

    @Mercury13
    Программист на «си с крестами» и не только
    Первое.
    Ваш код явно неортодоксальный, и, по-моему, он делает лишние сортировки.
    UPD. Алгоритмическая сложность такого кода порядка O(n² log n), более точную границу подобрать не могу, да и имеет ли смысл?

    Второе.
    Пусть массив отсортирован по убыванию, слева — одно число, справа — сто, делящий 100. Скажем, 101, [100], 99…0.
    Тогда i = 0, j = 101, и имеем массив 0, 100, 99…1, 101.
    Теперь i = 1, j = 100; arr[i]<pivot в пролёте, arr[j]>pivot в пролёте, i<j, и проводим обмен 0, 1, 99…2, 100, 101.
    На третьем шаге выходит 0, 1, 2 98…3, 99, 100, 101. И это при том, что делящий 100 и он давно встал на место.
    Когда мы прокрутим итерацию до конца, массив будет полностью отсортирован по возрастанию.
    Вывод: делящий элемент не обязательно будет точно делить массив, хоть многое что крутится вокруг него. И если будет сильный дисбаланс, часть элементов перейдёт из большего массива в меньший.
    Ответ написан
    Комментировать
  • Как посчитать сумму чисел в файле?

    @Mercury13
    Программист на «си с крестами» и не только
    Пока вижу вот что.
    1. a += buff[i]; Вы суммируете коды символов, а не их числовые значения. Правильно a += buff[i] - '0';

    2. Эти строки дублируют друг друга.
    fin >> buff;
      fin.getline(buff, 11);

    Сначала получаем 0123456789, потом файл кончился и на место 0 записываем нулевой символ (NUL).

    3. for (int i = 0; i < 11; i++) — неуниверсальная конструкция. Плюс цифр в строчке всего 10, поэтому надо i < 10.

    4. Так сумму чисел или сумму цифр?
    Ответ написан
  • Как реализовывается функцию(main), которая будет производить проверку?

    @Mercury13
    Программист на «си с крестами» и не только
    Мы пишем модульный тест собственными силами, без фреймворка. Это значит, что нам надо каким-то образом сымитировать работу фреймворка, но собственными силами и минимумом строчек.

    Для удобства нам потребуется одна функция — и немного препроцессорной магии.
    #include <iostream>
    #include <cstring>
    
    void doAssert(bool condition, int line)
    {
        if (condition) {
            std::cout << "Line " << line << " ok" << std::endl;
        } else {
            std::cout << "Line " << line << " FAILED" << std::endl;
        }
    }
    
    #define ASSERT(x) doAssert(x, __LINE__)
    #define ASSERT_STREQ(x, y) doAssert((std::strcmp(x, y) == 0), __LINE__)


    А теперь что-нибудь протестируем. Например, создание строки. Для простоты я проверю не вашу строку, а std::string.
    void testStringCreation()
    {
        std::string s;
        ASSERT(s.length() == 0);
        ASSERT(s.empty());
        ASSERT_STREQ("", s.c_str());
    }

    Проверим ещё операцию +=.
    void testPlusEq()
    {
        std::string s1 = "Alpha";
        std::string s2 = "Bravo";
        s1 += s2;
        ASSERT_STREQ("AlphaBravo", s1.c_str());
    }
    
    int main()
    {
        testStringCreation();
        testPlusEq();
        return 0;
    }

    Нужны ещё тесты — создавай новую функцию и вызывай её из main.
    Если какой-то тест не проходит — уничтожаем из main все вызовы, кроме несработавшего, и начинаем отладку.

    А теперь немного о том, какие должны быть модульные тесты.
    1. Изолированные. Если уничтожить часть тестов или запустить их в другом порядке, ничего не должно меняться — при условии, конечно, что никто не обращается к чужой памяти.
    2. Каждый модульный тест проверяет одну концепцию, которая указана в его названии. Например: строка создаётся, работает операция +=, и т.д. Будет очень много дублей кода, просто смиритесь с этим.
    3. Но нет ничего зазорного, что какой-то тест полагается на концепции, испытанные в других тестах. Если мы испытываем +=, то полагаем, что конструктор копирования работает, и не будем испытывать это.
    4. Модульным тестам иногда приходится иметь дело с внутренним поведением объекта. В таком случае объект должен выдавать наружу некоторую информацию — например, соответствующие функции обычно private, но по какой-то препроцессорной команде они становятся public.
    5. Модульные тесты обычно имеют дело с простейшим поведением объекта. Это не нагрузочные тесты, надо с этим просто смириться.
    6. Принцип работы теста таков. Создать исходные объекты, убедиться в том, что их ВНУТРЕННЕЕ (неспецифицированное, зависящее от реализации) состояние верное, провести некое действие, убедиться, что действие проведено правильно. Например, пусть строка выделяет память с запасом, и надо проверить, как она расширяется — тогда при присваивании "Alpha" мы убеждаемся, что выделено менее 10 символов, затем проводим s1 += "Bravo", затем убеждаемся, что выделено, например, 16 символов.
    7. Но заранее не нужно проверять, верно ли ВНЕШНЕЕ (заспецифицированное) состояние. Если мы пишем s1 = "Alpha", значит, строка равна "Alpha", и точка. Все случаи, возможные в операции string = char*, разумеется, покрыты другими тестами.
    8. Если вдруг в «боевом» коде обнаружится ошибка, надо сделать юнит-тест, её повторяющий, затем исправить ошибку и констатировать, что тест проходит и остальные тесты не «упали».
    9. То же самое, если ошибка случилась, когда проверяли другую концепцию. Проверяем +=, а глюк в конструкторе копирования — заводи новый тест, покрывающий эту ошибку.
    10. В проверках на равенство принят порядок ASSERT_EQ(что_должно_быть, что_реально_есть).
    Ответ написан
    Комментировать
  • В чем заключается проблема с кодировкой на хостинге?

    @Mercury13
    Программист на «си с крестами» и не только
    $resultData = mb_convert_encoding($resultData, 'Windows-1251', 'UTF-8');

    Думаю, автодетекция кодировки заглючила.
    Ответ написан
    2 комментария
  • На какой версии C++ Builder(максим. доступной) можно запустить проект C++ Builder 6?

    @Mercury13
    Программист на «си с крестами» и не только
    Переписывать придётся.

    1. Потому что в паскале — string и есть string, и раньше он был Ansi-, а теперь Unicode-. А на Си — именно что AnsiString/UnicodeString.
    2. То же самое касательно char. В Паскале-то можно объявить, что отныне char двухбайтовый, а на Си — есть стандарт, и по стандарту Си char — это один байт.
    3. Алгоритмы-то какие используются? Возможны какие-то алгоритмические уловки на манер алгоритма поиска подстроки Бойера-Мура — там тоже придётся переписывать.
    4. Если программа работает с файлами или сетью — надо указывать, как юникодные строки сериализовать. На Windows распространены UTF-16 Intel и UTF-8. На этот счёт AnsiString почти не используется в истинно юникодных программах из-за того, что хранит кодировку; если нужна цепочка байтов — есть RawByteString или std::string.
    5. Иногда старые программы работают с Юникодом через WideString (обёртку над BSTR). Сейчас этот WideString придётся массово менять на Unicode-.
    6. По-другому пишутся стандартные хедеры Delphi/VCL. Ранее #include <Forms.hpp>, сейчас — #include <Vcl.Forms.hpp>.

    Версия Builder — ну, любая, какую найдёшь. Не забудь только, что из C++11 есть enum class (не совсем стандартный, правда) и кое-что ещё. Я не проверял, как работает новый clang под XE10, но это только вопрос эффективности, «въедливости» и C++11 — миграцию на Юникод придётся проводить, как и раньше.

    Насчёт «въедливости». Я всегда считал, что G++ — самый «въедливый» компилятор. Но по статическому анализатору clang (встроен в Qt Creator, мой сейчас рабочий инструмент) кажется, что он переплюнул и этот рекорд. И сделать, чтобы один и тот же файл компилировался под древним Borland и суперсовременным clang — тяжеловато, по-моему. А именно это требовал Embarcadero от XE3 до XE8, если нужны версии и под x86, и под x64. Так что пусть BCC32 идёт туда, куда ему и дорога!

    Да, приходилось мигрировать на Юникод, когда появился Builder XE. Как я был рад!

    Знаю, что подхватит и сконвертирует XE2. Про более новые — не знаю ничего. Но даже если не сконвертирует — ничего не стоит построить новый проект с нуля и добавить туда все модули и формы. Но сконвертировать — это лишь половина вопроса; дальше придётся добиться компилируемости, а потом — и Юникода.
    Ответ написан
    Комментировать
  • Для чего используются "методы по умолчанию" на практике?

    @Mercury13
    Программист на «си с крестами» и не только
    Это приближает интерфейсы Java к примесям.

    1. Очень часто бывает, что у какой-то функции есть реализация, опирающаяся на другие функции — либо базовая неоптимальная, либо вообще единственно возможная. Чаще всего это избыточные функции-утилиты. Пишу на Си++
    class Stream {
    public:
      virtual void write(size_t length, const void* data) = 0;
    
      // пишет в поток word в машинном порядке байтов
      void writeW(uint16_t data) {
        write(2, &data);
      }
    };

    За что вообще так ненавидят множественное наследование? За дублирование данных! Вот у нас некая штука, без единого поля данных — почему в Java она класс, а не интерфейс?

    2. Какую-то функцию переопределяют настолько редко, что лучше сделать ей реализацию на месте. Опять Си++.
    class ErpConnector {
    public:
      // 80% модулей не могут экспортировать данные в систему управления
      //   предприятием — напишем базовую реализацию.
      virtual bool canExportData() const { return false; }
      virtual void exportData() const {}
    };


    3. И просто документирование, как оно должно себя вести :) Из реального проекта, снова Си++.
    void im::DateGrouper::toFirstDateOfPeriod(dt::Date& aDate, int aStart) const
    {
        aDate = toDate(toInt(aDate, aStart), aStart);
    }

    Класс служит для группировки дат в периоды (недели, месяцы, годы). aStart — это дополнительный int, позволяющий начать год с февраля или месяц с 15-го. Эта функция переводит дату «на месте» в первую дату периода. Она крайне неоптимальна (дату в число, затем число опять в дату) и потому переписана почти во всех в реализациях.
    void im::MonthGrouper::toFirstDateOfPeriod(dt::Date& aDate, int aStart) const
    {
        if (aDate.day < aStart)
            aDate.addMonthsMechanical(-1);  // механически вычесть 1 месяц; дата может стать неверной.
        aDate.day = aStart;
        aDate.fixupTo1();    // неверную дату привести к 1-му числу следующего месяца
    }


    Но по крайней мере понятно, как она должна работать.
    Ответ написан
    Комментировать
  • Как присвоить выражение boolean полю TObject?

    @Mercury13
    Программист на «си с крестами» и не только
    Попробую угадать задачу. Есть некое поле (назовём его UserData), но у нас-то пользовательские данные boolean!

    Решение 1. Прямое преобразование, невзирая на несовместимости типов.
    someObj.UserData := TObject(true);
    someBool := boolean(someObj.UserData);

    + Эффективнее всех.
    − Запрещено разыменовывать.

    Решение 2. Создать некий объект DummyObject. Обозначить nil=false, <>nil = true.
    someObj.UserData := dummyObject;
    someBool := (someObj.UserData <> nil);
    
    initialization
      dummyObject := TObject.Create;
    finalization
      dummyObject.Free;
    end.

    + Достаточно эффективно, можно разыменовывать.
    − Если в классе есть неотключаемое автоуничтожение UserData — облом!

    Решение 3. Создать класс TBoolObject.
    TBoolObject = class
    public
      Value : boolean;
      // конструкторы опущу, деструктор не нужен
    end.
    
    someObj.UserData := TBoolObject(true);
    someBool := (someObj.UserData as TBoolObject).Value;

    + Работает, когда в классе есть автоуничтожение UserData.
    − Иначе — овчина не стоит выделки, тогда нам эти объекты придётся уничтожать самим.

    Если же у вас тут экономия памяти и надо в одной памяти держать object или boolean, работает решение 4.
    TObjBool = record
    case integer of
    0 : ( asObj : TObject );
    1 : ( asBool : boolean );
    end;

    + Очень эффективно.
    − Программист сам должен следить за тем, что там: объект или буль.
    Ответ написан
    2 комментария
  • Что это означает?

    @Mercury13
    Программист на «си с крестами» и не только
    Это дело у вас на компьютере. L2TP — это самый простой вид туннелирования, некогда популярный в домосетях. Локальный трафик был бесплатный, интернет — дорогой, и надо сделать, чтобы интернет включался и выключался по желанию пользователя. Для этого у пользователя были два соединения: локальная сеть и VPN, и таблицей маршрутизации указывалось, какой трафик идёт в локальную, а какой — в VPN.
    Что наш L2TP делает сейчас, в 2016-м — я не знаю. Может, действительно кто-то на вашем компьютере скрывался от РКН…Правда, для этого ещё надо включить IPSec, 12 лет назад этого просто не было.
    Ответ написан
    Комментировать
  • Есть ли алгоритм для наиболее быстрого нахождения включает ли одна фигура другую?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Надо указывать не только то, что все точки первой фигуры внутри второй, но и то, что все точки второй фигуры снаружи первой.
    2. Даже примитивный алгоритм даёт скорость O(mn). Сколько у вас примерно точек?
    3. Можно воспользоваться эвристиками. Если охватывающий прямоугольник 1-й фигуры целиком во 2-й — ДА. Если вылезает из охватывающего прямоугольника 2-й — НЕТ.
    4. Можно поступить так. Отсортируем все точки той фигуры, которую проверяем на точки, по X-координате. Также отсортируем все отрезки той фигуры, которую проверяем на отрезки, по X-координате левой точки. Указатель отрезков ставим в начало перечня отрезков. Заводим список активных отрезков. Проходимся по всем точкам, поступая так…
    • Для всех отрезков в списке активных: если точка вышла из [X1, X2) — исключить из списка!
    • Смотрим на тот отрезок, что под указателем. Если X точки >= X1 — идём по фигуре дальше; если заодно X < X2 и Y выше отрезка (X1,X2 — (Y1,Y2) — включаем отрезок в список.
    При определённом устройстве списка (куча по X2) имеем O(n log n). И если меньшая фигура имеет сложность примерно как у большей (т.е. не логарифм) — имеем выигрыш.
    Ответ написан
  • Как решить проблему вывода из потока?

    @Mercury13
    Программист на «си с крестами» и не только
    #include <iostream>;
    Это не синтаксис Си, а директива препроцессора. Убери точку с запятой.

    #include <conio.h>;
    conio.h — это не стандартный хедер, а Windows-специфичный. Разумеется, вместо _getch() придётся использовать что-то другое.
    Ответ написан
    Комментировать
  • Движение персонажа под углом?

    @Mercury13
    Программист на «си с крестами» и не только
    Расскажу, как поступал я. Писал на Java ME, так что особо сложных идей не было.

    У каждой плитки была алгоритмически заданная поверхность (pavement). В более новых версиях движка приделали и потолок — впрочем, неважно.
    Для простоты введём несколько ограничений. Все они, разумеется, обходятся усложнённой логикой.
    • Ни на чём, кроме плиток, персонаж стоять не может. Тогда можно обойтись флагом: isSupported: true/false.
    • Поднимаясь по склону, персонаж не ударится головой в потолок. Кстати, у нас было два разных геймдиза и два разных подхода — когда один увидел этот баг, пошёл жаловаться, второй взял ограничение на карандаш и не делал таких уровней.
    • Неровности достаточно велики, чтобы можно было проверить ноги слева и ноги справа — и понятно, на какой высоте ему стоять.
    • Соотношение «площадь ног / размер хитбокса» достаточно велико, чтобы, пока персонаж падает, его гарантированно вытолкнуло из стены.
    В зависимости от скорости персонажа и крутизны склонов подберём epsilon — диапазон поиска поверхности.
    Предположим, наш персонаж стоит на чём-то и двигаем его вправо.
    1. Сдвинем его туда, где он должен быть.
    2. Попробуем подстроить высоту, взяв правую сторону ног и отыскав новую поверхность в диапазоне (y — epsilon, y + epsilon). То же самое с левой стороной ног. То, что выше — и есть наш новый y. Если не получилось — УПИРАЕТСЯ (см. шаг 3).
    3. Если на шаге 2 персонаж не упирается в стену, проверим на упор весь хитбокс. Если упирается — вытолкнем его из стены, снова подстроим высоту по принципу 2.
    Ответ написан
    2 комментария
  • Как округлить число до 2-3 знаков после запятой на "pascal.A.B.C" если выдается ошибка?

    @Mercury13
    Программист на «си с крестами» и не только
    Обычно в учебных работах (и даже не в учебных — именно так работает Excel) не округляют, а выводят с округлением.
    writeln(x1:0:3);
    writeln(x2:0:3);

    Каких-то продвинутых средств округления, которые округляют дробное число до i-го знака я не вижу.
    Кроме того, вы подсказали, с каким Паскалем вы имеете дело, я загрузил его и повторил прошлую ошибку — такая ошибка происходит именно что из-за округления NaN.
    Ответ написан
    Комментировать