Это сообщение Сэма Галсона, историка науки, ставшего инженером-программистом. Он является консультантом в 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 754NaN
). - Он кодируется по основанию десять, а не в двоичном формате.
- Он денормализован (т.е. существует более одного возможного представления для одного и того же числа).
- Половина округляется от нуля (указывается как комментарий в справочной реализации Крокфорда, а не в самом предложении).
Очевидно, проще иметь один числовой тип, если этого типа достаточно для всех задач. Специальные числа Infinity
и -Infinity
, предписанные стандартом с плавающей запятой IEEE 754, по той же причине заменяются одним значением NaN
. Базовые десять кодировок более интуитивно понятны для людей, потому что округления происходят там, где мы их ожидаем. Мы можем не догадываться, например, что 1/10 требует бесконечного расширения в двоичном представлении, и поэтому обычно оценивается примерно в 0,100000000000000006. И наоборот, никто не удивится, если вычислит 2/3 того, что десятичный тип сохранит его как нечто повторяющееся более 6,6. Наконец, денормализованное представление означает, что значащие цифры, включая нули, могут быть сохранены (поэтому 0,25 × 2 будет оцениваться как 0,50), что опять же полезно, когда число должно быть интерпретировано людьми. Округление от нуля - вот что большинство из нас учат делать в школе.
Рейнес не видел выступления, но когда я описал предложение, он упомянул несовместимость с существующими библиотеками как проблему. Предложение Крокфорда было разорвано в клочья в Hacker News людьми, которые считают целые типы полезными или обеспокоены производительностью. Основная проблема производительности состоит в том, что деление становится более сложным, тогда как при двоичном кодировании с плавающей запятой деление выполняется простым сдвигом битов. Кроме того, денормализованное представление означает, что перед сравнением чисел необходимо выполнить дополнительные вычисления. Infinity
values служат полезным целям, позволяя выполнять определенные вычисления, которые в противном случае потерпели бы неудачу. Голдберг приводит пример 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 Майка Колишоу.