Как стать автором
Обновить

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

Автор, извините, но насколько корректно называть это проблемой чисел с плавающей точкой? Является ли отсутствие рациональных дробей проблемой целочисленных типов? Вряд ли, согласитесь? Вот точно также и с плавающей точкой. Это особенность этих чисел, а не проблема. Проблема — неумение их использовать или незнание того, как они устроены внутри. Чисел с плавающей точкой нам полностью хватает для вычисления тех точностей, которые нужны нам в прикладном смысле. Да, там куча самых разных приемов, но тем не менее, двойной точности точно хватает за глаза. В очень редких случаях бывают нужны большие точности. Но и в тех точностях будет та же проблема, что и у вас.
Касаемо же самой простой задачи сравнения, мне кажется любой знакомый с плавающей точкой должен знать о небольшом эпсилон, который нужно использовать при проверке на равенство.
Напоследок, да, понятно, что этими числами не попользуешься в финансах. Но они и не были созданы для этого. У них достаточно узкий круг задач, с которыми они справляются на ура, если разработчики могут справиться с пониманием плавающей точки.

Когда-то давно я даже из интереса перебрал кучу языков изучая такое поведение. Единственный язык, где на тот момент 0.1+0.2==0.3 — MySQL. Причём в SQLite такое не работало. Pg не проверял. Так же на тот момент ещё не был популярен Rust, поэтому он тоже не попал в обзор

Заметьте, что 10/9 имеет идеальную точность. И всё, что нужно для точности, — это два кусочка информации. Это числитель и знаменатель. С помощью одного значения BigInt мы можем представлять произвольно большие целые числа. Но если мы создадим пару из целых чисел, то сможем представлять произвольно большие или маленькие числа.

Не произвольные числа, а только рациональные.
Число π или корень из 2 все равно не удастся представить с таком виде, даже имея бесконечный объем памяти
При особом желании иррациональные числа можно попробовать добавить. Для этого нужно и к числителю и к знаменателю добавить массив множителей. Класть в него Symbol('PI'), Symbol('e'), Symbol('sqrt(2)'). Ещё в библиотеке завести хэш этих иррациональных чисел чтоб не пересоздавать символы. Переписать все методы. Получится неплохой математический пакет. Но, естественно, это будет сильно дороже.

Да-да, и добавить туда же функции типа арксинуса, функцию Бесселя… И результат представлять в виде выражения: arcsin(arcsin(1/3)) в виде такой дроби, как вы предложили, вроде не представляется.
Правда, операции с такими числами будут дико ресурсоёмкими, а порой их сравнение произвести не удастся (равенство двух выражений – это, по сути, теорема, доказательство которой может оказаться весьма сложным). Зато абсолютно точно.


Ах да, это уже сделали. Называется "пакет символьной математики". Derive, Mathematica, Mapple. Имеют свою узкую нишу для применения, с "плохими" floating-point числами не сравнить.

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

Сравнение двух чисел в таком случае вообще будет неразрешимым по Тьюрингу.

А ничего, что иррациональных чисел будет побольше чем рациональных (множество которых счётно, т.е., эквивалентно множесту натуральных чисел)? Их нельзя так представить.

Их не больше. Вокруг каждого иррационального числа вьётся бесконечное количество его рациональных представлений. Бесконечное в прямом смысле.
Ну и что? Больше не в прямом смысле, а в смысле мощности множеств (а это вполне адекватный критерий для сравнения бесконечных множеств): мощность рациональных чисел счетно (их всех можно пронумеровать натуральными числеми), а иррациональных — напротив, не счетно. Это автоматически означает, что организовать их точное представление в конечной памяти нельзя. Можно представлять формальные «ссылки» на определённые иррациональные числа (типа ваших множителей), но по определению, чисел, которые вы не сможете представить точно, останется гораздо больше, чем тех, которые можно будет представить (т.к. представить вы сможете счетное множество в лучшем случае).

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

В таком случае, формульное представление (типа того, что выдаёт Wolfram|Alpha при упрощении выражения) будет как раз тем самым универсальным представлением. Но такое представление не пригодно для расчетов на простых машинах (с использованием стандартных библиотек мейнстримовых языков), потребуется как раз что-то похожее на вышеозначенную систему.

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

Я примерно то же самое чуть выше написал. Символьная математика – хорошо и круто, но для практических применений в большинстве случаев нужна не она, а что попроще, пусть и с потерей точности.

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

мощность рациональных чисел счетно (их всех можно пронумеровать натуральными числеми), а иррациональных — напротив, не счетно. Это автоматически означает, что организовать их точное представление в конечной памяти нельзя.

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

Вычислимых иррациональных столько же, сколько рациональных.

Их же несчётное множество, таким описанием не получится все добавить

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

В этом случае критика абсолютно не заслужена.

Язык, в котором операции типа 0.1 + 0.2 приводят на протяжении вот уже 15 лет к куче статей в интернете надо не критиковать, этот язык надо хоронить.

А так за статью плюс.

Почему только 15 лет? IEEE 754 приняли ещё в 1985м. Это особенность формата чисел с плавающей точкой, который поддерживается железом. Об этом было прекрасно известно ещё до появления Javascript в том же C например. Не бывает бесконечной точности при фиксированном размере. Предложение хоронить язык на таком основании очень смелое. Какие языки тогда останутся, Python?

Не останется, в python есть модули для работы с дробями, но float там аппаратный, 0.1 + 0.2 != 0.3

Удивительно. Тогда минус Python, например.
Я сейчас напишу немного не по теме, и скорее всего меня заминусуют. Но я вообще не силен в js, пару дней назад стал изучать react для создания mini apps в вк. И несколько раз чуть не заплакал. Наверняка, это из-за того, что я просто толком не разобрался в языке или фреймворке, но блин, что это за дичь???
image
Вариант сверху — нерабочий. Чтобы я смог в метод передать аргумент — надо вызвать анонимную функцию, которая вызовет этот метод. Почему? Зачем? Это проблема реакта или самого js?
И это только одна из приколюх с которой я столкнулся.
Потому что в первом случае вы вызываете метод и присваиваете onClick результат вызова, а во втором случае передаёте объект-лямбду с методом внутри как коллбек. Но такие вопросы и правда не по теме, лучше идите на Stackoverflow
Спасибо за ответ!

Проблема реакта, потому что такие проблемы должен решать фреймворк. Во vue и angular такого нет.

Потому что this указывает на объект в котором он вызывается, соответственно на элемент <button/> если его не обернуть в функцию. А если обернуть то на объект функции OnClick().
Логично ли это — да.
Очевидно — нет.
Но благодаря динамической природе this вам не нужно прокидывать "контекст".

Ну this-то тут причём?

Могу, только забинжу сначала :-)


Но ошибка-то в коде на картинке в другом, там написано <div onClick={this.doSome()}>

Ответ на ваш вопрос заключается примерно в этом:


typeof (2 + 2); // number
typeof (() => 2 + 2); // function

рекомендую посмотреть на то, во что компилируется ваш JSX код, чтобы исчезла "магия" и всё стало очень простым и понятным

Спасибо большое!
НЛО прилетело и опубликовало эту надпись здесь

Поставил минус в карму за быдлятину в примере кода — я не хочу видеть это дерьмо на хабре.

Да, признаю ошибку. Извините.

Правильно пишет MomoDev, функция — это тоже объект, а вы не передаете функцию, а вызываете её, после вызова присваивается её результат (в данном случае скорее всего undefined, функция ничего не возвращает). Вы хотите не просто передать в поле колбэка функцию, вы хотите привязать к этой функции аргумент ('second'). Для этого можно обернуть функцию в другую (прямо в поле или где угодно в замыкании, чтобы не создавать новую функцию с каждой перерисовкой), либо создать такуя функцию с помощью bind или apply
this.changePanel.bind(this, 'second')


Если бы вы не использовали jsx хотя бы на время изучения, вся последовательность «рендера» компонента была бы куда понятнее.
Cпасибо большое! Да, почитал об основах языза и ES6 и всё стало понятно. Извиняюсь за тупой вопрос и «быдлячничество»
this приобретает разное значение в разных случаях, почитай об этом habr.com/ru/company/ruvds/blog/419371
человек спросил именно про анонимную функцию где this = window, а не про синтаксис…

Чего? Это у вас changePanel-то анонимная функция? Или у вас в стрелочных функциях this каким-то образом равен window стал?

не знаю при чем тут changePanel

почти не использовал this, и думал что в анонимной функции он не будет ссылаться на объект (элемент)

С 2015го года прошло уже 5 лет, пора бы и узнать как в языке стрелочные функции работают…

все помнить невозможно, особенно когда это всего лишь расслабляющее хобби
Ruby! Какой глупый язык.
Clojure! Какой глупый язык.
Haskell! Какой глупый язык.

Я не видел ни одного «wtf js?!» с демонстрациями вида «0.1 + 0.2 != 0.3», потому что это довольно общеизвестная проблема среди большинства языков. С этим сталкиваются практически все в любой арифметике (за исключением отдельных математических движков вроде numpy/wolfram-mathematica, которые профессионально умеют вплоть до натуральных дробей), поэтому или принудительно округляют числа до какого-то знака перед прямым сравнением, или сравнивают их исключительно на «больше-меньше».

Некоторые ребята называют JS глупым языком из-за странных автоматических приведений типов, вроде
0 == [] //> true
//но
[] == ![] //> true
// и
10 + "20" //> "1020"
10 - "20" //> -10
10 + [1, 2] //> "101,2"
10 - [1, 2] //> NaN

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

А по плавающей запятой претензий что-то не видно. Если кто-то называет язык глупым по подобной причине, его можно смело, вообще без каких либо вопросов, гнать учить машинные представления чисел, до просветления. Или(!) даже заставить его выучить хотя бы один, любой, популярный язык: подобные претензии явно указывают, что оратор вообще не понимает о чём говорит и принципиально ничем не владеет. Вот ещё, на громких неучей срываться, и писать для них статьи, метать перед ними бисер. Они же даже не попытаются научиться, да и у них глобально другая цель: обозвать что-то «глупым», возвышая себя над целым комьюнити «тупиц, которые пишут на своём глупом языке».

Мне всё-таки кажется, что фраза "какой глупый язык" не более чем просто ирония. Не думаю, что автор действительно считает обозначенные языки глупыми.

Учитывая, что, по крайней мере, в хаскеле эти рациональные числа есть из коробки, а литералы вроде 0.1 — перегруженные:


λ> :t 0.1
0.1 :: Fractional p => p

то можно было бы просто использовать аннотацию типов у какого-нибудь одного литерала:


λ> 0.1 + 0.2 == (0.3 :: Rational)
True

ведь глупость только в том, что по дефолту выбирается Double (про что, кстати, есть ворнинг):


λ> :set -Wall
λ> 0.1 + 0.2 == 0.3              

<interactive>:6:1: warning: [-Wtype-defaults]
    • Defaulting the following constraints to type ‘Double’
        (Eq a0) arising from a use of ‘==’ at <interactive>:6:1-16
        (Num a0) arising from a use of ‘+’ at <interactive>:6:1-9
        (Fractional a0)
          arising from the literal ‘0.1’ at <interactive>:6:1-3
    • In the expression: 0.1 + 0.2 == 0.3
      In an equation for ‘it’: it = 0.1 + 0.2 == 0.3
False
10 + "20" //> "1020"
10 - "20" //> -10
10 + [1, 2] //> "101,2"
10 - [1, 2] //> NaN

Складывать разные типы данных не изучив принцип их конвертации это конечно сильно, но увы, зачем изучать язык — давайте сразу писать, а потом если что — компилятор с багами язык кривой.

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

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

А у меня у JS претензий и нет :) Но всё-таки свалиться с ошибкой лучше, чем продолжит выполнять код с непредсказуемыми последствиями. Хотя опять таки, это свойство всех языков с неявным приведением типов.

В языках с худо-бедно строгой типизацией, будут ошибки вроде «невозможно сложить число с массивом», когда извне приходит не то что ожидалось, и из-за этого может вылезать гора ошибок. И вот из-за этого некоторые ребята называют жаваскрипт дурацким, потому что при беглом прогоне как бы должна была выскочить ошибка, а её не было, всё как бы нормально, потом ошибка идёт дальше неотловленной.
Это только одно из мнений, их множество.
Непонятно, зачем в язык вообще закладывалась подобная функциональность. Как и 2 типа сравнений (стандартное, всеми используемое === vs. нестрогое == непонятно для чего). Вот какие вообще юзкейсы для подобных хаков?
В Python'е, например, строгая типизация, никаких лишних преобразований типов, и в результате он проще для изучения.
В старых Бейсиках тоже были функции STR$, VAL и др., и никаких автоматических конвертаций.
А в ЖабоСкрипте — сначала создаем проблемы на ровном месте, а потом их героически преодолеваем. Язык и так непрост для новичков (лямбда-выражения, особое прототипное ООП, правила использования this), так еще зачем-то дополнительно решили усложнить.
В Python'е, например, строгая типизация, никаких лишних преобразований типов, и в результате он проще для изучения.

И такие же два оператора сравнения: == и is.

В старых Бейсиках тоже были функции STR$, VAL и др., и никаких автоматических конвертаций.

Сейчас перепроверил: в VB4 (1995) уже были автоматические конвертации, и "1"+2 равнялось 3. Они сохранились в VBA и по сей день.

Нет, is и == это аналог == и .equals() из java, а не === и == из js.
В питоне и яве есть сравнение ссылок, а есть семантическое сравнение описываемое для каждого типа отдельно. А в js есть сравнение с приведением типов, а есть с проверкой равности типа.

А в js есть сравнение с приведением типов


Для чего? Хоть одна задача, где требуется именно == вместо стандартного ===? Совершенно ненужная, лишняя функциональность. Как и собственно приведение типов в целом. Интересно, откуда вообще взялась такая маразматическая идея?

Не, то есть я могу сейчас загнать на пол экрана про историческую перспективу js которая привела нас туда где мы есть. Или про полтора случая когда == правда удобнее === и три с половиной когда автоприведение типов по версии js это удобно.

Но я так и не смог понять, что вас заставило задать этот вопрос именно мне :)

Это обсуждение в блоге, вопрос ко всем :)

Ну, история известна. Создатель JS хотел разработать диалект Схемы, но в Mozilla решили, что это будет слишком сложно для пользователей. Пришлось в спешке переделывать, и получился непонятный мутант с синтаксисом Java и горой костылей. Но вот зачем было вводить в язык 2 типа сравнений, совершенно непонятно. За 8 лет, когда приходилось заниматься фронтом, я всегда использовал исключительно ===, и все знакомые поступают так же. Даже гуру Илья Кантор не смог придумать задач, где нужно нестрогое сравнение.

Так никто и не вводил два))

Сначала ввели один, потом сказали ойблин, но менять было поздно, так что ввели просто второй который работает как надо :)

А удобен == не как оператор нормального сравнения, а как такое странное сокращение когда в проекте все в курсе. То есть x == nullчитается как isNullOrUndefined(x). Это не прямо хорошо, но это распространённая идеома которая не вредит когда все привыкли. Слава богу x==xкак аналог Number.isNan(x) не многим короче и не получил такого распостранения. Что ещё... Ну все знают +x. Оно настолько распространено, что я сходу и не назову "правильный" способ парсить строку в число. Number(x) кажется?

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

let sum = 0;
numberStrings.forEach(s=>sum+=s);
Visual Basic запутан и усложнен так же как JavaScript. А вот в старых Бейсиках (например, спектрумовском или QBASIC) складывать числа и строки нельзя.
lang = if [] == "" then "JavaScript" else "Haskell"
main = putStrLn $ "I love " ++ lang

Простите.

А что смущает? Тип [] и "" одинаковый (с точностью до того, что первое выражение полиморфное) — строка ведь просто алиас для списка символов.

Ну так и для любого JS WTF есть какое-то объяснение. Я это к тому, что язык должен соответствовать своей спеке, а не ожиданиям каких-то людей.
Если [] == "" соответствует спеке Хаскеля, то меня ничего не смущает.

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


При этом для описания поведения [] == "" в хаскеле достаточно помнить, что строка — это в прямом смысле алиас для списка символов (ну вот прям type String = [Char]). Для описания поведения сравнения в JS нужно помнить больше вещей, как я понимаю.

Принцип наименьшего удивления – это маркетинговый ход Ruby, который привел к тому, что у них есть три метода для взятия длины строки (length, size и count). Чтобы уж точно никого не удивить.
В реальности этот принцип не работает, потому что у всех свои собственные ожидания от языка. Например, я ожидаю, что пустая строка кастанется к false, но Ruby считает по-другому.
Поэтому мораль простая: ты или знаешь спеку или не знаешь.

Для описания поведения сравнения в JS нужно помнить больше вещей, как я понимаю.

Достаточно запомнить правило «никогда не используй ==». А если забудешь, то статический анализ напомнит.
Поэтому мораль простая: ты или знаешь спеку или не знаешь.

Окей, в терминах спеки разница в том, какого она объёма и как часто туда надо смотреть. На JS я код как-то не писал, зато много и долго писал на плюсах. Там чтобы убедиться, что ты какой-то код имеешь право написать, спеку (большую, больше тыщи страниц) надо листать регулярно, и натыкаться на совершенно неочевидные места даже для совершенно тривиального кода. В хаскеле мне спеку открывать не приходилось вообще, наверное, никогда — ядро языка просто работает так, как ожидается.


Впрочем, если говорить о WTF-моментах, в хаскеле есть такой тайпкласс, как Foldable, описывающий сущности, по которым можно сворачиваться, типа списков, деревьев и так далее, и у него есть метод length (потому что длина — это, в конце концов, тоже свёртка). Из соображений удобства пары тоже реализуют Foldable, при этом учитывая только второй элемент (по вполне разумным причинам). Короче, в результате


λ> length (3, 4)
1

Это куда больший WTF-момент, чем [] == "", имхо.

Принцип наименьшего удивления – это маркетинговый ход Ruby, который привел к тому, что у них есть три метода для взятия длины строки (length, size и count). Чтобы уж точно никого не удивить.

Count совсем не для этого. Ну а length и size, по сути, псевдонимы. Они по факту вызывают одну и ту же функцию.
Некоторые ребята называют JS глупым языком из-за странных автоматических приведений типов, вроде


Ребята, у которых в реальном коде складываются числа с массивами, я извиняюсь, — говнокодеры. Если код нормально писать, никогда в эти тонкости приведения не придеться вникать.
С помощью одного значения BigInt мы можем представлять произвольно большие целые числа. Но если мы создадим пару из целых чисел, то сможем представлять произвольно большие или маленькие числа.

Можем, но практического смысла в этом нет, т.к. арифметические операции с рациональными дробями могут очень быстро раздувать числитель и знаменатель до достижения переполнения. Например, если в цикле складывать числа с разными знаменятелями, метод simplify перестанет справляться со своей задачей. Решая эту проблему, мы введем погрешность, заменив simplify на нормализацию и… изобретем float

До достижения переполнения чего? (Напомню, что тип BigInt в JS не ограничен в размере)

Ок, резиновый int не переполнися пока есть свободная RAM.
Но тогда появляется другая проблема — сложность арифметических операций будет пропорциональна размерам num, den. Сложность алгоритмов становится неопределенной.

V8 мне не разрешил сделать BigInt больше гигабайта.
0.1 + 0.2 не равно 0.3


Ну вот ни разу не встречал, чтобы именно это ставили в претензию именно Javascript'у. Откуда вы это взяли? Да и вообще, чтобы его называли глупым. Скорее он обманчив. Сначала многим кажется, что он простой. Некоторые вообще его называют самым простым языком из всех, да еще рекомендуют в качестве первого (рукалицо). Но когда начинаешь копать глубже, вдруг обнаруживается, что он вообще нифига не прост.
Что удручает, что очень многие люди на нем пишут, но практически не знают его. А когда человек не знает язык, он постоянно допускает ошибки и многие моменты он вообще не понимает, почему тут что-то работает так, а не иначе. Ну и людям, конечно, свойственно назвать язык глупым нежели взять и изучить его на нормальном уровне.

Ну, он очень простой по устройству. Гениально простой, ненамного сложнее Lisp.
А для использования… Ну, я не думаю, что он проектировался для использования в тех масштабах, которые мы имеем сейчас – только и всего. Если бы по прежнему речь шла о 10 строчках для создания необычного функционала на страничке – никто б не жаловался, это можно и на brainfuck написать.

И чем же он простой? Многие люди с трудом разбираются с тем, как на самом деле работает this, что же это такое за прототипная модель, что такое промисы, деструктурирующее присваивание или как на самом деле работает event loop и т.д. Да и часто не разбираются вообще, стараются пребывать в неведении, нежели сесть и разобраться ))) Javascript — это, по-моему, единственный в своем роде язык, когда большинство пишущих на нем людей не представляет, как он на самом деле работает, а в подавляющем большинстве случаев просто занимаются копипастом.
А если уж так посмотреть, то любой динамически типизированный язык не рассчитан на более чем условные 10 строчек кода. И тут Javascript не лучше и не хуже, чем любой другой динамически типизированный язык.
И чем же он простой?
Видимо имелся в виду вариант из середины 90-х, когда JS действительно был простым и понятным.
И тут Javascript не лучше и не хуже
Вот здесь интересный момент: есть три библиотеки, написанных на C, Python и JS. У них есть внешние зависимости. На С их 3, на Python 9, на JS подтягивается аж 705 внешних пакетов. Понятно, что это типично для JS экосистемы, но все же как-то это смущает, что может понадобиться искать ошибку в таком количестве модулей.
Видимо имелся в виду вариант из середины 90-х, когда JS действительно был простым и понятным.

Отчасти да. Только понятным он не был. Он был прост внутри – но примерно все понимали его неправильно. В силу сходства с Java ожидали и схожего поведения, для 10 строк кода это обычно срабатывало, а потом уже начинало ломаться.
В общем, жаль, что Айку не дали просто воткнуть в Netscape Scheme, дали задание на Java-подобный язык.


на JS подтягивается аж 705 внешних пакетов.

Ну, это психологический выверт, привнесённый, как я понимаю, node.js, а не самим языком. См. историю с left-pad.

Я сказал по устройству, а не по использованию :-).


По устройству прототипная модель проще, чем классы с VMT, this в JS сделан максимально примитивно (this задаётся при вызове. Для сравнения посмотрите реализации делегатов на C++. До стандартизации std::function было довольно уныло).
Промисы и деструктурирующее присваивание появились позже, но даже они устроены довольно просто. Это не значит, что при их использовании не выстрелишь себе в ногу (наоборот) – это значит, что сделать их реализацию на коленке можно за разумное время.

JS был моим первым языком, ничего плохого не случилось. Прототипы, промисы, this, деструктурирование, event loop и так далее хорошо понимаю, а в те времена, когда я его изучал, это и вовсе был es4, верстка под ie6 и так далее.

Я пишу о тех, кто не хочет во всем этом разбираться. И поверьте, таких людей хватает. Но хорошо, что такие не все.
А что нам за беда от проблем людей, которые не хотят разбираться? Это их личные трудности. Они на любом другом языке будут точно так же «думать, что программируют».
В любой другой язык они с большой вероятностью не полезут, особенно в компилируемый. Так как эти языки «прощают» некомпетентность в куда меньшей степени. Веб, он такой, туда идут все, кому не лень. Даже те, кто никогда бы не пошел в реальное программирование.
А проблема-то в чём? Мы же с Вами от этого не начнём писать код хуже?

es4 — это Action Script, вы наверное es3 имели в виду

Упс, да.

https://habr.com/ru/company/mailru/blog/335292/#tochnost-vychisleniya-01--02
Вот, например, на хабре. Вообще в гугле много результатов по этому запросу. В большинстве из них есть пометка, что претензия абсурдная.

Проблема здесь не в JavaScript
Конечно проблема не в этом. Проблема в том, что многие "современные программисты" далеки от железа и не знают что там делается под капотом. Да им этого знать и не надо — функцию позвал, она сделала что надо, а что там как и почему — их это не интересует.

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


Если им показать как сделать SPA без фреймворков и библиотек, у них еще и волосы дыбом встанут, и собес не пройдешь J


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


Поэтому я сейчас рынок специалистов воспринимаю как рынок каменщиков, где люди умеют ложить один конкретный кирпич, и всё. Дальше дай им сложную задачу, и всё рассыпется от отсутствия знаний и опыта, потому что дальше Реакта они не смотрели.

Честно говоря, завис на выражении
const one = new Ratio(BigInt(1), BigInt(0));


Я до этого думал, что второй параметр — это знаменатель, но ...

на языке PL/1 0.1+0.2 точно равно 0.3

Умножьте число на 100 или 1000, и будет вам счастье.


0.1 + 0.2 = 0.300...4


или если умножить на 100


(0.1 x 100) + (0.2 x 100) = 30
30 / 100 = 0.3


Или этот вариант может давать сбой?

В чём проблема проверить?
Эти выражения не равны

Только 30 на 100 делить не надо :-)


И да, после исправления ошибки получается true; но это случайность.

Кстати, интересный эффект. Если сравнивать с 0.3 то true, но с делением (30 / 100) выходит false, при том что 30 на 100 делится без погрешности (0.3).


Есть идеи почему работает именно так?
Насколько я понимаю, здесь нет проблемы с дробями, т.е. должно работать.

Потому что тут сравнивается 30 == 30/100. Ну конечно же это false. Как у вас true для 0.3 получилось — без понятия.

Автор, вы не точны в самом начале.
Число 0.1 НЕ представимо точно в двоичном виде. Поэтому компьютер использует его приближение, равное «0.100000001490116119384765625» (для 32-битного float). То же самое и с числом 0.2 — оно будет равно «0.20000000298023223876953125». А сложение компьютер всегда делает с абсолютной точностью, вот вы и получаете «неточный» результат. Просто надо помнить про то, что далеко не все числа представимы точно и НИКОГДА не использовать прямое сравнение в случае чисел с плавающей точкой. И все будет хорошо.

p.s. даже пи представимо примерно в компьютере, для 32-х бит это будет 3.1415927410125732421875, для 64-х бит это будет 3.141592653589793115997963468544185161590576171875 (я выделил точные знаки), как видите при любой точности для пи у вас будет только примерно треть точных знаков.

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

Да, наверное я неточно выразился. Финальный результат сложения зависит от представления чисел, поэтому для плавающей точки могут быть варианты, когда прибавление маленького числа даже не изменит результат. Например для float (32-bit): 16777216.0 + 1.0 = 16777216.0.
Дело в том, что в языках, имеющих дело с финансово-экономическими расчетами (вроде PL/1), т.е. там, где требуются точные расчеты, числа делятся не на целые и действительные, а на точные и приближенные.
Если в языке нет точных чисел никакие масштабные коэффициенты или предложение хранить деньги в виде целого числа копеек не помогут.
В том же PL/1 точка является признаком только дробной части, а вот показатель степени «E» — уже признаком приближенного числа.
Поэтому в PL/1 0.1Е0+0.2Е0 не равно точно 0.3Е0 как и в других языках (как и в примере).
А вот 0.1+0.2 точно равны 0.3 поскольку это точные числа, представленные в двоично-десятичном виде.
Кто вообще может шутить про js? Про инвалидов шутить не красиво!
если бы график был маленьким, а значения данных были большими, я бы получил ошибки округления. И зачастую это нормально. Но на графике некоторые пиксели должны выстраиваться в линию. Иначе рисунок выглядит неправильно.


Проблема выглядит слегка надуманной. Не совсем понимаю, каким образом в свечном графике можно заметить несоответствие пикселей из-за округления.
А так, рациональные числа это забавно, но узок круг применения. Стоит попытаться применить их к каким-то кругам/тригонометрии/площадям — так всё и сломается.
Но JavaScript точно не заслуживает возмутительных шуток.


То есть вы нашли (а может сами придумали) одну-единственную действительно глупую шутку, и на основании этого сделали вывод что JavaScript не заслуживает возмутительных шуток? Нет, заслуживает, ещё как. Просто пример выбран неудачный.
А хотите удачный? Нет 64-битных целых. Да что там, вообще нет целых, но реально напрягает именно отсутствие 64-битных. То есть чтобы разобрать например json (JavaScript Object Notation, на секундочку) с 64-битными целыми, придётся сначала регулярками (или как-то ещё) превратить эти числа в строки, потом разобрать json, а потом уже как-то обрабатывать эти строки, вероятнее всего преобразовав в bigInt. Множество операций для решения проблемы, которая в здравых языках вообще не существует. И это — только один из примеров неадекватности языка.
Отличная статья. Проблеме (или особенности, если угодно) чисел с плавающей точкой посвящен целый сайт: 0.30000000000000004.com. Что касается JS, то равенство 0.1 + 0.2 и 0.3 можно проверить следующим образом: 0.1+0.2-0.3 < Number.EPSILON. Обычно, как отмечено в одном из комментариев выше, рекомендуется приводить дробные числа к целым, осуществлять над ними необходимые операции и выполнять обратное преобразование

Тогда уж 0.1 + 0.2 - 0.3 < Number.EPSILON * 0.3

В большинстве задач эта точность неважна

Для вывода пользователю каждый новичок возьмет на вооружение простое округление

Math.round((0.1 + 0.2)*10)/10

или

+(0.1 + 0.2).toFixed(1)
НЛО прилетело и опубликовало эту надпись здесь
let okr = (value, cc) => +value.toFixed(cc)
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Учитывая, что это единственное полезное применение унарного плюса — можете считать его оператором явного преобразования в число.
О, Господи, человек делает сравнение натуральных дробей через циклы, и потом чему-то пытается учить и делает вокруг этого библиотеку! Вмеcто того, чтобы сравнивать их в одно целочисленное действие:
const equals = (one, two) =>
    one.numerator * two.denominator === two.numerator * one.denominator;
И так же со сравнениями.
Здесь собрано гораздо больше глупых шуток.
Спасибо, что пытаетесь развенчать миф об ущербности js даже из-за банально «0.1+0.2»
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.