Учебный видео-курс по арифметике с плавающей запятой в формате IEEE-754. Часть I

    Арифметика с плавающей запятой хорошо понятна далеко не всем программистам. Раньше я работал в разных IT-фирмах и с удивлением обнаруживал, что даже опытные программисты теряются, когда возникает задача подобрать $\varepsilon$ для сравнения двух чисел с плавающей запятой в коде вроде такого:

    if (abs (a-b) < EPS) . . .

    Они наивно выбирали одно и то же число типа 1e-8 для всех своих проектов, создавая тем самым потенциальное место проявление суровых ошибок. Более того, они пытались сравнивать два числа типа double вот так:

    if (a < b) . . .

    не понимая, за что же я их ругал, когда видел подобные глупости. Я уже не говорю о том, какой ужас могла вызвать константа типа 0x400921fb54442d18 (всего лишь число $\pi$), которую можно увидеть в некоторых старых программах или на экране отладчика.



    Однажды в процессе отладки коллега обнаружил, что выражение с double не меняется, когда он пытается сложить два числа, и тогда он стал грешить на ошибку в компиляторе или отладчике, пока я не объяснил, в каких случаях сумма двух чисел останется равной только одному из слагаемых.

    Фокусы с перестановкой чисел также пугают многих, и на StackOverflow мне нередко приходилось видеть недоумённые вопросы пользователей о том, почему небольшое изменение выражения приводит к различным результатам и почему оптимизация кода может привести к совершенно неправильному ответу. Однажды кто-то из пользователей многозначительно заявил, что нашёл ошибку в процессоре, когда перевёл целое число из 64 битов в число удвоенной точности, затем обратно — и получил другое число. О том как при таком преобразовании теряются минимум 11 битов он, видимо, не знал. И подобное невежество встречается даже в среде тех, кто уверен, что он-то как раз знает как всё работает.

    Даже на Хабре нередко попадаются статьи, в которых авторы «разоблачают» непонятное поведение чисел с плавающей запятой и выдают своё открытие за что-то новое и неизведанное, неочевидное и загадочное. Странно видеть такие статьи, написанные вроде бы профессиональными программистами. Пиарить их не буду, ищите, пожалуйста, сами.

    Казалось бы, различные учебные материалы должны решить проблему, но нет. Как правило, читатель отпугивается формулами и непонятными словами, которыми его сразу закидывает автор: нормализованное число, скрытая единица, смещённая экспонента — и далее без объяснения причин, по которым появилось то или иное решение в Стандарте IEEE-754, начинается сухое изложение теории. Видеолекции на эту тему также не блещут разнообразием: все как будто разбирают один и тот же примитивный пример, где всё быстро и красиво получается… и ученик никогда не догадается, что почти 100% компиляторов работают с плавающей арифметикой с ошибками. Это и понятно, такие преподаватели сами не знают что откуда берётся и как работает, а потому и рассказывают только то, что им самим понятно, причём совершенно очевидно, что они сами только вчера открыли для себя мир арифметики с плавающей запятой, но уже спешат сбивчиво о нём рассказать. Не осуждаю, но считаю, что такое поведение на публике недопустимо.

    Поэтому я решил попытаться исправить ситуацию и создал в каком-то смысле пробный учебный курс. Это видео-курс, который плавно погружает зрителя в мир арифметики с плавающей запятой. Первые четыре урока мы рассматриваем десятичную систему счисления и то, как обстоятельства вынуждают нас создавать то одну, то другую систему чисел, приходя в конце концов к системе с плавающей запятой именно в таком виде. Откуда берётся нормализация и зачем она нужна? Откуда берётся проблема ассоциативности? Как и почему теряется точность и что с этим делать? Чего делать категорически нельзя и почему? Почему в такой системе возникают денормализованные числа и что это вообще такое? Затем последующие 4 урока показывают как все эти знания красиво ложатся на двоичную арифметику и откуда там появляется идея скрытого бита. Когда на экране возникает первая «страшная формула», зритель уже имеет в голове нужный образ арифметики с плавающей запятой и легко понимает логику такой формулы… конечно, если правильно выполнял упражнения. Все эти 8 уроков — первая часть курса, она для новичков. Вторая будет уже для продвинутых программистов и сейчас находится в разработке.

    Почему видео, а не текст? Объяснение простое: я пробую разные форматы и убедился, что новичкам читать трудно. Кто умеет читать внимательно, тот откроет учебник и прочитает текст с формулами, разберётся и поймёт. Кому читать тяжело, кого пугают сложные вещи и кому проще изучать материал за чашечкой чая — тому подходит плавное погружение в видео формате с закадровым голосом. Много воды? Да, возможно, но курс рассчитан даже на тех людей, которые хотят программировать, но не дружили с математикой в школе. Поэтому то, что для вас «вода» — это не вода, а то, что вы уже хорошо знаете из школы, а многие мои ученики — нет. Будьте к ним снисходительными, все мы с чего-то начинали. А ещё видео можно рассматривать как подготовку к чтению серьёзных учебников. Согласитесь, приятно, когда открываешь учебник и значительно быстрее понимаешь то, что в нём написано, так как нужный образ уже в голове.

    О себе: в прошлом профессиональный преподаватель, 11 лет работал в вузе, преподавал математику и программирование, в последние годы занимаюсь разработкой математических библиотек для высокопроизводительных вычислений. Я хорошо понимаю то, чего хочет моя целевая аудитория, и хорошо понимаю что по этой теме востребовано в мире программирования, а потому считаю, что вправе создавать подобные курсы, и вы можете убедиться сами, что аналогичных по качеству (в плане содержания) лекций на русском языке вы сейчас не найдёте. Проверьте! Первые четыре урока, из которых вы уже узнаете много интересного, совершенно бесплатны. Если понравится, можете пройти остальные, они ещё интереснее, но уже за плату. Чужой труд нужно уважать: я не продаю знания, но мне нужна поддержка для продолжения образовательного труда, поэтому моё время стоит денег. Вообще о качестве моего труда можете сделать выводы по другим моим статьям на Хабре.

    Для тех кто вступит в наше сообщество ВК я могу предоставить скидку 50%, если обратитесь в ЛС. Пожалуйста, регистрируйтесь в школе ZealComputing (это бесплатно) и смотрите первые 4 урока. Либо они же есть на YouTube (первый здесь — и далее по ссылкам из описания). Да, вступительное видео, на которое я там ссылаюсь, можно не смотреть, это просто реклама.

    Краткое содержание платных уроков


    Урок №5: Впервые переходим к двоичной системе счисления. Строим красивую и простую модель числа с плавающей запятой на основе 6 битов, предельно близкую к формату IEEE-754. Это самый важный и самый сложный урок. Предыдущие четыре урока были призваны показать откуда в арифметике с плавающей запятой берутся те или иные вещи, а теперь вы понимаете, как эти вещи красиво возникают в формате IEEE-754 на игрушечном и понятном примере.

    Урок №6: Знакомимся с округлением. Оно не так очевидно, как в привычной математике. Вы узнаете то, что трудно увидеть в простейших примерах, приводимых другими видео-преподавателями. А именно: перевести число из десятичной системы счисления в формат IEEE-754 бывает иногда трудно настолько, что некоторые компиляторы не умеют делать это правильно. Подробно объясню почему в теории всё так просто, а на практике — нет.

    Урок №7: Здесь вы полностью овладеваете форматами binary32 и binary64 (float и double), показываю как можно на C++ выводить битовое представление чисел (на других языках тоже можно, но не на всех, там я отсылаю вас к гуглу или яндексу и показываю как это просто, например, отыскать решение для Java). После этого урока структура чисел с плавающей запятой (если вы хорошо выполнили упражнения) вам полностью понятна и не может вызывать вопросов, на которые не было бы ответа в предыдущих уроках.

    Урок №8: Практические рекомендации по применению арифметики с плавающей запятой. Некоторые уже описанные особенности и новые моменты: потеря коммутативности, ассоциативности, неожиданные проявления так называемой «невозмутимости». И самый важный совет! Этот совет поможет вам избежать почти 100% всех ошибок в типовых неответственных задачах. Далее пойдёт обсуждение ошибки двойного округления, катастрофическая потеря значащих цифр: когда и как возникает. В общем, вся простая практика, не требующая высшей математики, описана в этом уроке.

    Что ещё входит в курс? А большее ничего и не нужно! Вы можете задавать мне вопросы по теме уроков, но я уверен, они не будут возникать. Каждый урок содержит исчерпывающие упражнения с ответами, поэтому моё участие в общем-то и не требуется, отсюда и невысокая цена. Полноценный курс с преподавателем, общением, практикумом под руководством наставника и живыми лекциями стоил бы в десять раз больше.

    Приятного обучения!

    Комментарии 5

      0

      Вот вопрос, который меня больше всего удивляет, почему в Rust нет обобщённого трейта "это Float", позволяющего вызову функции диктовать какой из f32/f64 там внутри.

        0
        Хм. Ну про if через эпсилон понятно — под каждые пару чисел нужно подбирать свои эпсилон (либо автоматически, либо, если порядок чисел более-менее известен заранее, определённой константой, а не одной на всё), а вот в чём проблема при прямом сравнении ">" и "<"?
          0

          Ответ на данный вопрос есть в предложенном курсе.

            0
            Лучше скажите, в чём проблема, а вот как решить — уже на курсе. С сравнением через E много где пишут, а вот чтобы сравнивать на больше-меньше я не припомню.

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

      Самое читаемое