igrishaev
671 subscribers
197 photos
7 videos
8 files
433 links
Ivan Grishaev on everything // grishaev.me
Download Telegram
Числа №13 #numbers

Еще одна кулстори на тему чисел с плавающей запятой.

Мы привыкли, что железо не ошибается, но иногда бывает обратное. В 1994 году Intel выпустила первые процессоры линейки Pentium. Процессоры эти печально прославились тем, что неправильно считали некоторые флоаты (инструкция FDIV). Вот это правильный результат:

4195835,0/3145727,0 = 1,333820449136241002

А вот что выдавал бракованный процессор:

4195835,0/3145727,0 = 1,333739068902037589

Казалось бы, ошибка начинается с четвертой цифры после запятой, пустяки. Но вот что случиться, если поделить и умножить на одно и то же число:

(4195835/3145727)*3145727 = 4195835

против

(4195835/3145727)*3145727 = 4195579

Разница 256 – прилично.

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

Реакция Intel была потрясающей. Во-первых, в фирме знали о неверных операциях с числами, но считали, что проблема коснется только узкого круга лиц. Во-вторых, компания предложила замену процессора только тем, кто докажет, что испытывает проблемы. Мол, если ты хомяк, купивший процессор ради Doom, тебе и так сойдет.

К счастью, реакция общества, конкурентов и даже партнеров Intel была крайне негативной. Уже тогда процессорам доверяли лифты, оборудование, самолеты. Было ясно, что процессор, который неверно считает – это тикающая бомба. В результате Intel все-таки заменила процессоры, и общий убыток составил 475 миллионов долларов. Даже сегодня это много, а в 1994 году сумма была космической.

Проблема Пентиума коснулась и софта. В компиляторы Delphi, Basic и других языков добавили костыль: если текущий процессор равен "Pentium такой-то", то операции с числами эмулируются программно. Это и замедление вычислений, и лишний код в компиляторе.

Больше Intel не позволяла себе таких фокусов. Наработки Томаса Найсли включили в процесс верификации процессоров, чтобы не допустить ошибок в будущем.

Почитать об ошибке Pentium FDIV можно в Википедии; есть краткая версия статьи на русском. На Ютубе по словам Pentium FDIV ищутся подробные разборы ошибки.
👍84🔥4
Числа №14 #numbers

Мы много говорили о числах с плавающей запятой. Есть и другой формат – дробные числа с фиксированной запятой. Давайте кратко их рассмотрим.

Об этих числах я упоминал в заметке о первой Playstation. Это эмуляция дробных чисел для архитектур без математического сопроцессора. Главное их преимущество в том, что все операции с ними выполняются как целочисленные.

У фиксированных чисел, как ясно из определения, дробный разделитель не плавает туда-сюда. Например, мы решили, что после запятой хранится два разряда, и так будет всегда. Не нужно нормализовать числа и приводить их к общим экспонентам.

Число с фиксированной запятой хранится как целое, умноженное на некий коэффициент. Для простоты рассмотрим десятичные числа. Будем хранить цены в рублях с двумя десятичными знаками (копейками):

Кофе 80.99
Булочка 115.50


Коэффициент равен 10^-2 (или 0.01), а их целые представления равны:

Кофе 8099
Булочка 11550


Складываются и вычитаются такие числа в одну машинную команду. Найдем сумму покупки:

1
8099
11550
-----
19649


Приведем к дробному виду, чтобы были видны копейки:

19649 * 10^-2 = 196.49

Это было сложение, а теперь рассмотрим умножение. Умножать булочку на кофе немного странно, но давайте не думать об этом.

Числа 80.99 и 115.50 можно записать таким образом:

80.99 = 8099 * 10^-2
115.50 = 11550 * 10^-2


Их произведение:

8099 * 10^-2 * 11550 * 10^-2

Две степени десяти схлопываются в одну: -2 + -2 = -4

8099 * 11550 * 10^-4

Умножение на степень десяти равносильно тому, чтобы сдвинуть разряд влево или вправо. Произведение 8099 * 11550 дает 93543450, и нужно сдвинуть разделитель влево на 4 позиции. Результат:

9354.3450

Однако мы храним только две цифры после запятой. Поэтому хвост 3450 округляется до двух знаков. Цифра 5 округляется вверх, и на конце оказывается 35:

9354.35

Целое (внутреннее) представление становится таким образом 935435.

Вот как устроены числа с фиксированной запятой.

Выше мы рассматрели их в десятичном виде, потому что так удобней. В двоичном виде происходит то же самое. Договариваются, что первые 16 бит хранят целую часть, а остальные 16 – дробную. Сокращенно записывают так: Q16.16. Могут быть и другие варианты: больше бит на целую часть (Q24.8) или наоборот – на дробную (Q8.24). Вместо десятичного сдвига происходит битовый силами процессора.

С такими числами работали игровые консоли вроде Playstation, Sega Saturn и GameBoy. Очевидный их минус в том, что, во-первых, диапазон значений гораздо ниже, чем у float. Во-вторых, нужно следить за переполнением целой части. Чтобы не допустить переполнения, числа приводят к аналогам, где у целой части больше бит. Например, конвертируют Q16.16 в Q24.8, выполняют расчеты, а затем возвращаются к Q16.16.

Раньше числа с фиксированной запятой использовались часто. Например, в Doom для расчета углов; в LaTeX для растеризации шрифтов; в ранних играх Blizzard (Starcraft).

В следующей заметке я хочу рассказать о Старкрафте: какую роль в нем играли числа с фиксированной запятой и почему разработчики не использовали целые.
👍52
Докладываю: праздновал Пасху как образцовый христианин. С утра красили яйца, дети были в восторге. Испекли маленький тортик, ходили в церковь. Мое отношение к ней вы знаете, но я никогда не прочь зайти и поглазеть.

В церкви все как обычно, правда некоторые иконы странные. Изображена отрезанная голова на блюде, а рядом бутылка и нож. Любят все-таки христиане такую жесть.

Целуют иконы, а рядом даже нет антисептика! В ковидные времена и то был. Будет ли оскорбительным побрызгать на икону и протереть? Она же под стеклом, картина не пострадает.

Стоит бак со святой водой и стаканчиками. Младшая хотела попить, но я запретил. Сказал, что не кипяченая. Задумался: теряется ли святой дух при кипячении? Надо так: сначала кипятить, а потом святить.

Пока супруга молилась и ставила свечи, я должен был отвлекать детей. Купил циклопическую свечку – чуть ли не со световой меч. Дети опять-таки были в восторге. Продавщица сказала, что с такой свечой можно идти к самому алтарю, что я и сделал.

Свечка долго не зажигалась, плюс потребовались усилия, чтобы укрепить ее в латунном стаканчике. Все же я как-то справился; свечку поставил за кота.

Если серьезно, то я давно думаю вот о чем. Бесспорно, Иисус – самый популярный человек в мире. Даты его рождения и смерти признаны официальными праздниками во многих странах. В день рождения Пушкина мы работаем, а Иисуса – нет. Кроме того, не так много в мире людей, от даты рождения которых мы отсчитываем эры. Один, если говорить точнее.

Я задаюсь вопросом: будет ли когда-нибудь новая, Третья эра? Кто ее определит и что он такого сделает? Я полагаю, такая эра будет – через двадцать, тридцать, сто тысяч лет. Другое дело, что именно произойдет? На мой взгляд, не катастрофа в духе Голливуда или эпидемия супер-гриппа а-ля Стивен Кинг (читали "Противостояние"? ).

Наверное, в Третьей эре возникнут люди, для которых делать плохие вещи будет немыслимо. Скажем, убивать, лгать, извлекать благо из страданий других. Текущая ситуация такова, что при особых условиях все из этого списка не только законно, но даже поощряется государствами и религиями. Всегда найдется лазейка – поправка к закону или цитата святого – чтобы оправдать ту или иную мерзость. Может, каким-то чудом это перестанет работать в будущем, и совершать поступки, которые так или иначе ведут к страданиям других, будет не принято.

Это и будет новая эра. Отсчитывать ее будут, конечно, от даты рождения того, кто первым предложил это в блоге.
😁124👎1🔥1
Вчера закончили с дочкой Гарри Поттера – все семь книг. Это заняло год и пять месяцев. Я вычислил срок с помощью блога: оказалось, в январе 2025 года я публиковал маленькую заметку о денежной системе в мире ГП.

О каждой книге можно сказать разное: где-то лучше, где-то хуже. Кто-что затянуто; какие-то сцены никуда не ведут; от некоторых персонажей можно было отказаться. Однако в целом ГП – замечательный, монументальный труд. Это новая вселенная по образу Звездный Войн, и по ней еще долго будут делать приквелы-сиквелы, фанатский контент и прочее.

Гарри Поттер – это современная детская литература. Ключевое слово "современная", то есть описывает персонажа нашего времени. Несмотря на все нелепости вселенной ГП, ключевые темы вроде отношений, любви, долга, преданности совпадают с ориентирами нормального современного человека. При этом книга взрослеет вместе с читателем.

Я жалею лишь об одном: мы читали ГП в неудачном переводе. Нам подарили первые несколько книг, и на тот момент мне были безразличны все эти Снейпы-Злеи. Когда я разобрался, то уже был полный комплект, и покупать новый не хотелось. Считаю, что переводчиков Махаона, которые исковеркали все имена, нужно отпинать и сбросить в канализацию. Может, так они поймут, что нельзя портить чужой труд.

Прикупил пару книжек о ГП на английском. Развивает: много прилагательных, разговорных оборотов, художественные вещи, с которыми не имеешь дела на созвонах.

ГП — важная веха современности. Читайте детям Гарри Поттера. И детям хорошо, и вам понравится.
👍14😁5
Один кудрявый разработчик сказал: design is about keeping things apart. Дизайн – это удежание вещей по отдельности. Полная цитата звучала так:

design is fundamentally about taking things apart (decomplecting) so they can be managed, understood, and put back together (composed) in a flexible way

Смысл в том, что пока элементы более-менее свободны, свободен и дизайн. Следовательно, свободны и мы в принятии решений.

Сегодня я осознал, насколько это важно.

Где-то полгода назад я запрограммировал сложный процесс. Много шагов и состояния, работает почти час, поднимает тысячу лямбд. Ширина картинки с диаграммой – 10 тысяч пикселей. Я этой сложностью не горжусь: была бы моя воля, я бы сделал проще и вообще по-другому. Но меня заставили сделать так.

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

А мне говорят: уж если есть убер-комбайн, давай педалить его. Я свое мнение защитить не смог и проиграл. В итоге на пайплайн навесили кучу других операций.

И вот вчера случилась классика. Мне понадобилось вызвать процесс еще раз, но с особыми параметрами. Ну знаете, такой же, но с перламутровыми пуговицами (с). Новое бизнес-требование. Я запустил, и все эти допики, которые навесили другие разработчики, посыпались. Где-то создались дубли, где-то не те поля и так далее. Пришлось чистить и откатывать.

И вот теперь я должен пройтись по всей цепочке шагов и добавить if-else. Если режим запуска такой-то, то не вызывать эту примочку, не обращаться в этот сервис, не выполнять этот запрос и так далее. Фактически – вынести все обвесы в условие.

В итоге у нас things нифига не apart, и design тоже не задался. А если бы вынесли в отдельные пайплайны вместо того, чтобы бесконтрольно усложнять исходный, дело бы обошлось.

У докладов Рича Хикки двойное дно. Ты их смотришь и не понимаешь: кажется, что дед несет пургу. А через много лет понимаешь, но уже через боль. Жаль, что понимание приходит именно так.
15👍7🔥3😢2👏1💯1
Еще одно наблюдение: люди, которые пользуются ИИ, любят врать. Уж не знаю, как это происходит; скорее всего от того, что пользу ИИ хочется доказать так отчаянно, что идут во все тяжкие, даже если не бьется с реальностью.

Есть у нас на работе чат-бот для выполнения команд. Спрашиваю коллег: как поднять такой-то сервис? Отвечают: /start service foobar . Хорошо, выполняю /start service ABC. Бот пишет: понял-принял, запускаю сервис XYZ – то есть совершенно другой.

Спрашиваю у коллег: что это ваш бот чудит, не то запускает. Они такие: ты ввел не то, машина ведет себя правильно, неверный ввод, ошибка пользователя. Ну-ну.

Другой пример: сделали консольный ИИ-тул, чтобы управлять инфрой. Смотреть бакеты в S3, логи, таски в джире. Выглядит так, что прямо сказка. Запускаю – вылетает с кодом -3. Ни единой строчки информации.

По косвенным признакам я понял, что проблема с Node.js. Тула пытается скачать особую версию, не может, пытается прописать что-то в bashrc, тоже не может... короче, не только не работает, но даже не говорит, в чем дело.

Как вы уже поняли, на самом деле все работает. Просто я не то ввел, у меня не та версия, не та система, это не то, другое не то. А так все работает.

Дорогие иишники, пожалуйста, хватит врать себе и каждому. Программа не работает, если она не работает. Пишите программы, которые работают. А которые не работают, не пишите. Очень простой принцип.
👍18😁12🤔1
Числа #15 #numbers

Пока бы закончить с числами; осталось две заметки.

В прошлый раз я рассказывал о числах с фиксированной запятой и о том, как они использовались в играх. В том числе упомянул первый Старкрафт (BroodWar), о котором и хочу поговорить.

Старкрафт – это стратегия реального времени фирмы Blizzard; вышла в 1998 году. Она стала прорывом во многих областях и надолго задала планку. Старкрафт отличался запредельным числом игровых механик и их комбинаций. Расы были кардинально разными: зерги делали ставку на массу, протоссы – на мощь, терраны – на технологии. Юниты были наземные и летающие, транспортные, невидимые, с обычным уроном и массовым. Были псионики – юниты, которые атаковали только магией.

Игра, хотя и была двумерной, поддерживала многоуровневый ландшафт, и это влияло на исход боя: юниты ниже мазали по тем, что выше.

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

Добавьте к этому отличную кампанию, полную пафоса; крутые CGI-ролики; сетевую игру в компьютерном классе; Battle.net для счастливчиков с интернетом в 98 году – и станет ясно, что игра была прорывом. До сих пор она остается одной из главных киберспортивных дисциплин, а в Южной Корее – национальным видом спорта.

Чтобы обеспечить все это добро, движок считал много чисел. В 98 году не все процессоры поддерживали плавающие числа, поэтому в Старкрафте использовались числа с фиксированной запятой. Код игры никогда не открывали, однако ее устройство ясно из публикаций разработчиков и специального SDK для ботов. Последнее особенно интересно: Старкрафт был первой игрой, под которую писали ИИ-ботов. Были даже шоу-матчи между известными игроками и ботами по аналогии с шашками Go и AlphaStar. Ежегодно проходят турниры для разрабочиков ботов.

Интересен следующий факт: числа, что мы видим в игре, на самом деле являются дробными. Скажем, у морпеха (марика) 40 единиц здоровья, атака 6, броня 0. У зилота (zealot, воин протоссов) здоровье 100 единиц, защитное поле на 60 пунктов, атака 16. Все числа выглядят как целые, удобно складывать и вычитать их.

На уровне кода каждое число – это контейнер с фиксированной запятой. Тип, который его описывает, называется fp8, что означает fixed point 8. Восьмерка подразумевает число битов, отведенных под дробную часть. Таким образом, fp8 хранит числа с точностью до 1/256 или 0.00390625 в десятичной системе.

Я не видел полного описания этого типа. Однако логично предположить: если он занимал 16 бит, то на целую часть оставалось 8 бит. Таким образом целая часть вмещала диапазон от -128 до 127. Это мало. Поэтому скорее всего тип занимал 32 бита (обычный int), и на целую часть отводилось 24 бита вместе со знаком. Диапазон таком образом был от -(2^23) до (2^23)-1 или от -8,388,608 до 8,388,607.

Восемь миллионов – достаточно, чтобы покрыть все случаи игры, включая особенные. Например, юнитов-боссов в кампании с колоссальным запасом здоровья.

И все-таки: зачем игре дробные числа? Мы ведь выяснили, что у зилота 160 здоровья, а у марика 6. Это значит, потребуется 27 атак, чтобы марик победил зилота. Все удобно, но нет – за этим удобством кроется фиксированная запятая.

Причина в следующем: если все числа целые, то в какой-то момент они портят игровой баланс. Такая сложная игра как Старкрафт обязана учитывать фракции, чтобы не вышла досадная вещь, которую мы рассмотрим ниже.

Про морпеха вы уже знаете; а у зергов есть юнит под названием ультралиск. Это слонопотам, который хорош против морпехов. Изначально у него 1 единица брони. Всем юнитам зергов можно сделать три апгрейда на броню +1. Наконец, ультралиску доступен его личный апгрейд +2 к броне. В сумме они дают 6 брони.

Единица брони снижает урон на единицу. А теперь смотрите: ультралиск со всеми апгрейдами имеет 6 брони, а морпех без апгрейдов на атаку наносит 6 урона. 6 - 6 = 0. Если задача решается в целых числах, то морпех наносит нулевой урон, и ультралиск полностью для него неуязвим!
Проблему можно решить тем, чтобы обязать игрока, который играет за терранов, сделать апгрейд +1 для мариков. Тогда морпех будет наносить единичку урона: 6 - 7 = -1. Но это слабое решение. Общая беда в том, что если у юнита высокая броня, он неуязвим для всех юнитов с атакой ниже брони.

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

На самом деле броня в 6 единиц – это не показатель -6 ко вражеской атаке. Это коэффициент ее снижения. В Старкрафте и других играх Blizzard броня устроена логарифмически. Вспомним как устроен логарифмический рост: на малых значениях он почти линейный. Значения брони 1 или 2 дают снижение урона на 1 и 2 соответственно. Однако далее рост замедляется: показатель 3 снижает урон не на 3, а на 2.9, 4 – на 3.5 и так далее.

Числа я беру из головы, потому что не готов запускать Старкрафт и все это проверять. Однако такая же механика справедлива для Warcraft, Diablo и других игр Blizzard. Логарифмическая броня – это залог того, юнит никогда не станет неуязвим. Даже если выдать юниту 100 брони, урон по нему будет снижен до условных 0.01 здоровья. Да, это мало, и здоровье будет тикать по единичке в минуту. Но урон все-таки будет, и сотня-другая обычных юнитов смогут победить тяжеловеса.

Как следствие, даже ультралиска с броней +6 можно победить морпехами без апгрейдов. Каждый марик будет наносить не 0, а 0.25 урона (условно). Разумеется, игроку за терранов в такой ситуации ничего не светит, но дизайн игры в этом не виноват. Не получится создать юнита, который полностью неуязвим, и это правильно.

Должно быть, вы замечали в других играх: долбишь врага, а здоровье убывает на единичку каждый сотый удар. На самом деле его здоровье — дробное число и урон наносится тоже дробный. В интерфейсе все показатели выглядят целыми, чтобы не смущать игрока длинными хвостами. Кому понравится 2.513523734353450003 урона?

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

По той же причине в раннем Старкрафте был баг: "мертвые" юниты с нулевым здоровьем. Такой юнит мог воевать и погибал только от следующего удара. Как вы поняли, он не был мертвым: целая часть здоровья была нулевой, но оставались единичные биты в дробной части. Это случалось из-за показателей брони, сплеш-урона с затуханием и многих других факторов. Старкрафт не округлял дробные числа: чтобы показать целую часть, он просто сдвигал fp8 на восемь битов вправо. Результат был 0, но юнит считался живым. Позже это исправили.

Да, многое я бы мог рассказать про Старкрафт – сколько времени в него прощелкал! В качестве бонуса предлагаю цикл статей разработчика, который пишет ботов и понимает устройство игры как бог.
👍6🔥4
Один из худших атавизмов в программировании – это протекающий оператор switch/case. Он ведет родословную со времен Си и в настоящее время есть во всех императивных языках.

switch (expression) {
case constant1:
// Code to execute if expression == constant1
break;
case constant2:
// Code to execute if expression == constant2
break;
default:
// Code to execute if no case matches
}


В чем проблема? В том, что без оператора break управление передается в ниже. Если expression подходит под разные условия, сработают две ветки одна за другой. Счастливой отладки.

Сколько людей погорело на протечках в switch/case – я даже боюсь предположить.

В Джаве switch/case устроен аналогично, но к счастью там это поправили. Есть другой switch/case со стрелками, который гарантирует, что будет выполнена только одна ветка. Вдобавок он возвращает значение:

int result = switch (input) {
case "A" -> {
System.out.println("Processing A");
yield 1;
}
case "B" -> {
System.out.println("Processing B");
yield 2;
}
default -> 0;
};


В последних Джавах много внимания уделяют паттерн-матчингу. Можно сказать, что любой switch лучше свести к паттерн-матчингу, потому что иначе получается минное поле из-за протечек.

В Кложе, например, макрос case гарантирует, что сработает только одна ветка. При этом выражения должны быть литералами, потому что компилятор вычисляет от них хеши для таблицы переходов:

(case (:status item)

"active"
(process-active item)

"pending"
(process-pending item)

;; default
(throw-error "wrong status"))


Есть макрос cond для условий, которые вычисляются в рантайме. Будет вычеслена первая ветка, для которой условие вернет истину:

(cond

(-> item :status (= "active"))
(process-active item)

(or (-> item :status (= "pending"))
(queue-is-blocked ...)
(process-pending item)

:else
(default-case "..."))


Наконец, есть скользящий, "протекающий" cond со стрелкой. Он принимает пары (условие->выражение) и пропускает сквозь них исходное выражение.

(cond-> []

:always
(conj (get-some-item ..))

(item-is-active? item)
(conj the-item)

:finally
(into (get-additional-items)))


Пишу я это потому, что погорел со switch/case в Питоне. Хотя с версии 3.10 в него завезли оператор match, многие до сих пор пишут каскады if...elif...elif...elif...else. И вот я добавил новую ветку в середину, а вместо elif написал if:

if action == "foobar":
do_this()

elif action == "kek":
do_that()

if action == "some_action":
do_lol()

else:
throw Exception("...")


Ветка выполняется, но первый оператор if обрывается и начинается второй. Для него условие не находится и срабатывает else, где бросается исключение. Получилось так, что действие выполняется, но из-за того, что было исключение, задача запускается еще раз. В итоге мой код вызвал действие три раза. Линтер ничего не заметил.

На мой взгляд, старого оператора switch/case в коде быть не должно – только паттерн-матчинг, который гарантирует, что сработает только одна ветка. А второе – не знаю, как можно жить с языком без макросов. Это словно красть у себя самого.
2👀2
Числа №16 #numbers

Почему в Джаве байт имеет знаковый тип? Скажем, если прочитать из файла байт 249, то при печати увидим -7. Откуда минус?

Дело в том, что в Джаве тип byte и его старший брат Byte – знаковые.

Знаковые числа – те, что тратят первый бит на признак того, есть ли впереди минус. Например, число 3 в битовом виде записано так:

00000011

А с минусом – так:

10000011

Первый бит означает, что впереди минус. Если прочитать этот набор битов как беззнаковое число, то первый бит дает 128 (два в степени 8). Итоговое число становится 128 + 2 + 1 = 131.

Это как раз случай Джавы. Она смотрит на биты 10000011 как на знаковое число, поэтому первый бит нарисует минус, а оставшиеся 0000011 – тройку.

В интернете есть цитата Гослинга:

For me as a language designer, which I don't really count myself as these days, what "simple" really ended up meaning was could I expect J. Random Developer to hold the spec in his head. That definition says that, for instance, Java isn't -- and in fact a lot of these languages end up with a lot of corner cases, things that nobody really understands. Quiz any C developer about unsigned, and pretty soon you discover that almost no C developers actually understand what goes on with unsigned, what unsigned arithmetic is. Things like that made C complex. The language part of Java is, I think, pretty simple. The libraries you have to look up.


Кому лень переводить: причина в том, чтобы сделать проще. В языках С и С++ есть знаковые и беззнаковые типы, и когда они выступают вместе, никто не знает, что произойдет. Да, в стандартах Си описаны правила, но легко ошибиться. Поэтому проще всего исключить беззнаковые числа.

Мое мнение таково: да, беззнаковые short, int, long лучше исключить. Пусть будут только знаковые. А вот с байтом вышла ошибка.

Беда в том, что в Джаве байт считается числом: тип Byte унаследован от Number. Я не согласен. Байт – это не число. Он никогда не интересует нас в качестве числа. Когда в последний раз вы прибавляли число к байту? Или умножали байт на байт? Вычитали их?

Байт – это группа битов, элемент алфавита из 256 символов. Да, ему можно сопоставить число для удобства, но не более того. Байт – это сырые данные, на которые можно смотреть по-разному в зависимости от контекста. Это может быть знаковое число или беззнаковое; это может быть несколько битовых групп, например, первые три – тип, а остальные пять – значение.

В байте может умещаться крошечное число с плавающей запятой! Бит на знак, три на экспоненту и четыре на мантису:

1 101 1010

Такие числа называются tiny float 8 и поддерживаются видеокартами.

Факт, что отрицательные байты никем не используются, очевиден. Скажем, IP-адрес – это четыре байта через точку, но никто не записывает 192.168.0.1 как -64.-88.0.1. Ни один hex-редактор не показывает отрицательные байты, не важно в hex- или decimal режимах.

Тот же Турбо Паскаль разделял байт и знаковое 8-битное число: типы Byte и ShortInt. Первый фигурирует, когда читаешь сеть или файл. Второе – это преобразование группы бит к конкретному типу.

На примере Джавы можно вспомнить цитату Эйнштейна: вещи должны быть простыми, но не проще этого. Идея избавиться от беззнаковых типов в целом хороша, но повлияла на работу с байтами. Байты не должны трактоваться как числа, а если это допустимо, то знака не должно быть. Мы ведь не считаем буквы от -16? Так что с байтами упрощение пошло во вред.

* * *

Это была последняя заметка о числах – других заметок на эту тему в черновиках у меня нет. Надеюсь, вам понравилось, и кто-то что-то для себя извлек. Напомню, что все заметки про числа легко найти по тегу #numbers. Позже я соберусь с силами и перенесу их в блог.
👍127🔥1🤔1🤡1
Посмотрел одно видео, захотелось пересказать.

В Нью-Йорке построили очень высокий и тонкий небоскреб. Выглядит как карандаш или шприц: высота 435 метров (1428 футов), а ширина сечения сравнима с теннисным кортом. Один этаж – одна квартира. Небоскреб предназначен для жилья, а не офисов. Это апартаменты для богачей, и слова "лакшери", "worldclass" так и сыплются с экрана.

Интересно, сколько проблем решили инженеры, пока строили эту иглу. Дело в том, что здание испытывает нешуточное давление от ветра. Чтобы оно не упало, сделали множество тросов толщиной с футбольный мяч, которые уходят глубоко под землю. Таким образом у здания корневая система в точности как у дерева.

Далее: вещи под названием турбулентность и резонанс. Когда воздушный поток встречает препятствие, он порождает хаотичные волны по его бокам. Хаотичность эта, однако, со временем входит в резонанс и раскачивает препятствие вправо-влево (а не вперед-назад, как можно подумать). В результате раскачивания "корни" рано или поздно будут вырваны.

Чтобы это устранить, здание обшито рельефными плитами и напоминает сверло. За счет специальных канавок воздушный поток разделяют в разные стороны, и его давление падает. Кроме того, каждый 30-й этаж не застроен, а вместо него сквозное отверстие для ветра.

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

Когда здание почти достроили, случился шторм. Башня крана – на высоте 400 метров – сорвалась с креплений и стала неуправляемо вращаться. Выглядело это жутко. Никто не погиб, но в итоге кран заехал стрелой по готовому фасаду. Были убытки и репутационные потери.

Был и другой инцидент со льдом. Известно, что чем выше поднимаешься, тем холоднее. На высоте 400 метров даже в хорошую (на поверхности земли) погоду перепад температуры может быть значительными. На вершине здания стал скапливаться лед, и позже он обвалился на тротуары. На видео показано, как глыба льда размером с бревно падает рядом с такси. Популярности зданию это не добавило.

И вот небоскреб достроили и стали заселять. Центр мира – Америка, Нью-Йорк, Манхэттен. Вид – потрясающий, в ясную погоду простирается на километры. Ты властелин, а мелкие твари копошатся у твоих подошв.

Однако выяснились интересные детали. На высоких этажах здание все-таки шатается, и это можно почувствовать! Люди описывают это как крайне криповое ощущение. Также покачивания порождают шум: металл и бетон, как бы ни были крепки, деформируются и скрипят.

Шума добавляет система насосов. Чтобы поднять воду на 400 метров, нужные мощные насосные станции, которые шумят. Да, изоляция звука, все дела. Но полностью скрыть звук от них невозможно.

Лифты. Во-первых, ехать с условного 150 этажа на первый – долго. Во-вторых, смена давления. Со школы мы знаем, что каждые 12 метров давление падает на один миллиметр ртутного столба. 400 метров – 33 миллиметра долой. Немало людей чувствительны к резкой смене высоты из-за давления.

И вот здание стоит пустое. Кто-то, конечно, купил там квартиру, однако в целом оно – призрак. Огромный гвоздь стоимостью словно из золота, который воткнули неизвестно зачем. Очередной урок, что так делать не надо.

Выводы я оставлю на следующую заметку.

Видео: https://www.youtube.com/watch?v=-WEtpnKOBjs
👍72