Это сообщение Сэма Галсона, историка науки, ставшего инженером-программистом. Он является консультантом в YLD, помогая клиентам создавать масштабируемую и отказоустойчивую инфраструктуру с контейнерами, Node.js и React. Эта запись впервые появилась в блоге YLD.

FullStack - это самопровозглашенная «конференция по JavaScript, Node, Angular и IoT в Лондоне». Я пошел на это, так что, думаю, это справедливо. По мере того, как утихают вихри и водовороты трех напряженных дней разговоров о JavaScript, некоторые фрагменты медленно всплывают на поверхность. Я представляю их здесь.

Любопытно, что некоторые из самых интересных разговоров касались двоичного кодирования и программирования непосредственно с помощью единиц и нулей в JavaScript: это биты. Другие памятные беседы были связаны с досками Интернета вещей: это детали… конечно!

Утонувший Гиппас

В то время, когда кажется, что машины берут верх, приятно напомнить, что большинство из них не могут выполнять основную арифметику. Как объяснил Дуглас «» Крокфорд во вступительном слове конференции, Числа, компьютеризированная арифметика сталкивается с двумя основными проблемами: ошибками переполнения / потери значимости и округления.

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

Неспособность исправить эти ошибки привела как минимум к двум крупным трагедиям. В 1996 году ракета Ariane 5 (в разработке более 7 миллиардов долларов) была запущена у побережья Французской Гвианы, пролетела 37 секунд, а затем взорвалась. Его скорость была больше, чем ожидалось, и расчет в системе наведения дал число, превышающее выделенные 16 битов, что привело к сбиванию ракеты с пути.

В 1991 году, во время войны в Персидском заливе, американская ракета «Пэтриот» не смогла перехватить приближающийся иракский «Скад», в результате чего погибло 28 человек. Счетчик 0,1-секундных интервалов внутренних часов ракеты был умножен на несколько часов, чтобы вычислить время в секундах, но поскольку 0,1 не представляется точно в двоичной системе с плавающей запятой, ошибка округления умножалась и приводила к отказу системы слежения за ракетой.

Выступление Крокфорда (забавно описанное председателем Джерардом Сансом как хорошее сочетание фактов и истории) включало красивый исторический обзор числового представления и его пределов, а в конце трогательные признания Фибоначчи и Лейбница. Мне напомнили Гиппаса, утопленного богами за разглашение существования иррациональных чисел. Мы все еще занимаемся его наследием.

JS Math беды

Как ни странно, Крокфорд был не единственным, кто углубился в числовое представление в FullStack. В серии выступлений Атана Рейнса, представляющих stdlib, новую библиотеку численных наук для Node и браузера, были затронуты похожие вопросы. Математика, машинное обучение и JavaScript сделали JavaScript языком машинного обучения будущего. Рейнс считает, что скорость JavaScript и возможности взаимодействия с C / WebAssembly более чем достаточны для удовлетворения требований машинного обучения к производительности, и объясняет текущую нехватку внимания JavaScript в этой области отсутствием хороших библиотек и интересом сообщества. Сомневаюсь, что я единственный, кто надеется, что stdlib смогу это исправить. А пока было интересно узнать больше о технических проблемах, связанных с точными математическими вычислениями в JavaScript. Оказывается, многие встроенные математические функции, такие как Math.cos и Math.sin, оптимизированные по скорости, а не по точности, дают разные результаты в разных браузерах без каких-либо стандартов, определяющих приемлемую точность. Это делает библиотеку Math практически бесполезной для надежных математических вычислений (полный список проблем Math JavaScript можно найти в репозитории stdlib). К тому же многие внешние библиотеки плохо написаны. Рейнс выделил реализации функций разности квадратов как часто становящиеся жертвой так называемого катастрофического аннулирования. Рассмотрим два способа вычисления суммы x² - y²:

В плохой функции, если x и y очень близки, но не идентичны, большая часть значащих цифр будет уничтожена вычитанием, а ошибки округления, вызванные умножением, будут преобладать в конечном результате (это катастрофическая отмена). Хорошо написанная библиотека ограничит влияние ошибок округления путем вычитания y из x перед выполнением умножения. В последующих докладах Надстройки Node.js для высокопроизводительных числовых вычислений и WebAssembly и будущее Интернета Рейнес подробно рассказал о некоторых практических шагах, необходимых для внедрения C и код Fortran в stdlib. Наконец, в The JavaScript Data Science Survival Kit Филипп Буркхардт, другой участник stdlib, показал, как использовать stdlib для построения графика данных и построения простого классификатора спама.

Наименьший общий знаменатель

Крокфорд закончил свое выступление, предложив, чтобы следующее поколение языков программирования приняло новый числовой тип, который он называет DEC64, 64-битное десятичное число. Основная цель этого предложения, по-видимому, состоит в том, чтобы сделать работу с числами более интуитивно понятной для людей за счет того, что компьютеры должны выполнять больше работы. Его ключевые особенности:

  • Он предназначен для использования только числовым типом.
  • Он имеет только одно специальное значение NaN, которое равно самому себе (в отличие от IEEE 754 NaN).
  • Он кодируется по основанию десять, а не в двоичном формате.
  • Он денормализован (т.е. существует более одного возможного представления для одного и того же числа).
  • Половина округляется от нуля (указывается как комментарий в справочной реализации Крокфорда, а не в самом предложении).

Очевидно, проще иметь один числовой тип, если этого типа достаточно для всех задач. Специальные числа Infinity и -Infinity, предписанные стандартом с плавающей запятой IEEE 754, по той же причине заменяются одним значением NaN. Базовые десять кодировок более интуитивно понятны для людей, потому что округления происходят там, где мы их ожидаем. Мы можем не догадываться, например, что 1/10 требует бесконечного расширения в двоичном представлении, и поэтому обычно оценивается примерно в 0,100000000000000006. И наоборот, никто не удивится, если вычислит 2/3 того, что десятичный тип сохранит его как нечто повторяющееся более 6,6. Наконец, денормализованное представление означает, что значащие цифры, включая нули, могут быть сохранены (поэтому 0,25 × 2 будет оцениваться как 0,50), что опять же полезно, когда число должно быть интерпретировано людьми. Округление от нуля - вот что большинство из нас учат делать в школе.

Рейнес не видел выступления, но когда я описал предложение, он упомянул несовместимость с существующими библиотеками как проблему. Предложение Крокфорда было разорвано в клочья в Hacker News людьми, которые считают целые типы полезными или обеспокоены производительностью. Основная проблема производительности состоит в том, что деление становится более сложным, тогда как при двоичном кодировании с плавающей запятой деление выполняется простым сдвигом битов. Кроме того, денормализованное представление означает, что перед сравнением чисел необходимо выполнить дополнительные вычисления. Infinityvalues ​​служат полезным целям, позволяя выполнять определенные вычисления, которые в противном случае потерпели бы неудачу. Голдберг приводит пример 1 / (x + x⁻¹), который правильно оценивается как 0, когда x равен 0, благодаря правилам о Infinity. Наконец, известно, что округление на половину от 0 в среднем приводит к дрейфу значений вверх, поэтому IEEE 754 вместо этого использует округление вдвое до четного (банковское округление). Это может быть наименее оправданный пункт в предложении Крокфорда, но от него можно отказаться без ущерба для остальных.

В свете этой критики было счастливым совпадением, что поддержка хотя бы одного аспекта предложения Крокфорда должна была материализоваться случайно в форме анекдота, изложенного Майлсом Боринсом в выпусках Node.js, как они работают? . К этому времени, конечно, мои уши были готовы к мелочам, связанным с числами. Боринс описал, как он случайно ввел регрессию в querystring.parse, передав свой maxKeys аргумент непосредственно в String.prototype.split в качестве второго аргумента (ограничение на количество возвращаемых разбиений). Патч нарушил обработку случая, когда maxKeys равно Infinity, потому что, когда split приводит свое предельное значение к длине массива, он обрабатывает Infinity как NaN, то есть длину 0. Конечно, эта ошибка будет иметь было бы невозможно, если бы Infinity не существовало. Крокфорд определенно прав в том, что невыразимые ошибки вызваны сложностями, связанными с числовыми типами, даже в строго ограниченной среде JavaScript. Однако маловероятно, что его предложение получит отклик в ближайшем будущем.

Биты и пиксели

Если Крокфорд и Райнс пытаются абстрагироваться от некоторых проблем битового уровня, которые беспокоят нас при выполнении арифметических операций, Биты браузера Javascript Бена Фоксолла побудили нас задержаться в глубине души, продемонстрировав ряд впечатляющих эффектов, достигнутых с помощью манипулирование двоичными данными в браузере. Чтобы доказать себе, что ответ узла лжет о значении 0,1, попробуйте следующее:

(0.1).toPrecision(20)
// '0.10000000000000000555'

(0.1).toString(2);
// '0.0001100110011001100110011001100110011001100110011001101'

parseInt('1100110011001100110011001100110011001100110011001101', 2)  
// 3602879701896397

(2/3602879701896397) / 100
// 5.551115123125783e-18 --> the secret appendage of 0.1!

Кто может объяснить, почему это работает? Прокомментируйте, пожалуйста!

Используйте операторы OR и NOT, чтобы раздражать коллег:

const floored = 3.145|0  // 3  
!!~str.indexOf(word)     // true if string contains word

Более продуктивно используйте API-интерфейсы браузера для извлечения необработанных данных изображений и звука в типизированные массивы:

/* canvas API */
imageData = ctx.getImageData(0, 0, width, height)  
const data = new Uint32Array(imageData.data.buffer)  
// do something to data
ctx.putImageData(imageData, 0, 0)

/* web audio API */
const timeData = new Uint8Array(analyser.fftSize)  
analyser.getByteTimeDomainData(timeData)  
var freqData = new Uint8Array(analyser.frequencyBinCount)  
analyser.getByteFrequencyData(freqData)

Поскольку вы можете передавать один и тот же базовый буфер в разные типизированные массивы, вы можете легко преобразовать массив из 8-битных значений r, g, b, a в массив 32-битных значений rgba с помощью Uint32Array. Как позже показал Райнес, может быть полезно использовать это совместное использование памяти, чтобы избежать перераспределения памяти при обработке больших объемов данных. В докладе Бена было еще много уловок для создания эффектов со звуком и пикселями, которые вы можете проверить здесь.

Кусочки действия!

Наконец, краткий обзор некоторых интересных бесед, касающихся Интернета вещей. Основной доклад Эми Дэнсби Назад в будущее: революция в области создания Интернета вещей был сплоченным призывом к экспериментам. У меня просто нет времени, - услышал я чье-то бормотание. Они явно пропустили JavaScript и Bluetooth LE, в которых Гордон Уильямс показал, как подключить Espruino к новому Bluetooth API Chrome с помощью Puck.js за две минуты. Демонстрация Ника О'Лири использования Node-RED для создания конвейеров перетаскивания, связывающих устройства Интернета вещей за секунды, могла бы служить той же цели. Микроконтроллеры как микросервисы Ника Хера были дополнением к Tessel и призывом сохранить его локально: обслуживайте свои демонстрации с микроконтроллера, а не из облака! Наконец, призыв к эксперименту был полностью удовлетворен Tiny Computers, JavaScript и MIDI Джорджем Мандисом, который продемонстрировал различные приложения, использующие API браузера для взаимодействия с устройствами MIDI. Следите за обновлениями: я буду вставлять текст моего следующего сообщения в блог с помощью MIDI-флейты.

Дальнейшее чтение

Большинство выступлений FullStack доступны на сайте. Проверь их!

Лучшее введение в проблемы, связанные с компьютерной арифметикой, - это книга Голдберга Что должен знать каждый компьютерный ученый об арифметике с плавающей запятой. Как следует из названия, вы должны прочитать это, если нет. Чтобы глубже погрузиться в варианты десятичного кодирования, посмотрите Speleotrove Майка Колишоу.