У меня есть следующий тестовый скрипт:
function test() {
var x = 0.1 * 0.2;
document.write(x);
}
test();
Это напечатает результат, в 0.020000000000000004
то время как он должен просто напечатать 0.02
(если вы используете свой калькулятор). Насколько я понял, это связано с ошибками в точности умножения с плавающей точкой.
У кого-нибудь есть хорошее решение, чтобы в таком случае я получил правильный результат 0.02
? Я знаю, что есть такие функции, как toFixed
или округление было бы другой возможностью, но я бы хотел, чтобы на самом деле было напечатано целое число без каких-либо вырезок и округлений. Просто хотел узнать, есть ли у кого-нибудь хорошее, элегантное решение.
Конечно, иначе я округлю до 10 цифр или около того.
0.1
конечное двоичное число с плавающей запятой.Ответы:
Из руководства с плавающей точкой :
Обратите внимание, что первый пункт применяется только в том случае, если вам действительно нужно конкретное точное десятичное поведение. Большинству людей это не нужно, они просто раздражены тем, что их программы не работают правильно с числами, такими как 1/10, даже не осознавая, что они даже не будут мигать при той же ошибке, если это произошло с 1/3.
Если первая точка зрения действительно применима к вам, используйте BigDecimal для JavaScript , который совсем не элегантен, но фактически решает проблему, а не предоставляет несовершенный обходной путь.
источник
console.log(9332654729891549)
собственно отпечатки9332654729891548
(т.е. от одного!);P
... Между2⁵²
=4,503,599,627,370,496
и2⁵³
=9,007,199,254,740,992
представимые числа являются точно целыми числами . Для следующего диапазона, от2⁵³
до2⁵⁴
, все умножается на2
, так представимых чисел являются даже те , и т.д. С другой стороны , для предыдущего диапазона от2⁵¹
до2⁵²
, дистанционирование0.5
, и т.д. Это связано с простым увеличением | уменьшение базы | radix 2 | двоичный показатель в / из 64-битного значения с плавающей запятой (что, в свою очередь, объясняет редко документированное «неожиданное» поведениеtoPrecision()
для значений между0
и1
).Мне нравится решение Педро Ладарии и я использую что-то подобное.
В отличие от решения Pedros, оно округляется до 0,999 ... повторяется и с точностью до плюс / минус один на младшей значащей цифре.
Примечание. При работе с 32- или 64-разрядными числами с плавающей запятой для достижения наилучших результатов следует использовать toPrecision (7) и toPrecision (15). Смотрите этот вопрос для информации о том, почему.
источник
toPrecision
возвращает строку вместо числа. Это не всегда может быть желательным.(9.99*5).toPrecision(2)
= 50 вместо 49,95, потому что toPrecision считает целое число, а не только десятичные дроби. Затем вы можете использоватьtoPrecision(4)
, но если ваш результат> 100, то вам снова не повезло, потому что это позволит первым трем числам и одному десятичному знаку сместить точку и сделать ее более или менее непригодной. Я закончил тем, что использовалtoFixed(2)
вместо этогоДля математически склонных: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Рекомендуемый подход заключается в использовании поправочных коэффициентов (умножьте на подходящую степень 10, чтобы арифметика происходила между целыми числами). Например, в случае
0.1 * 0.2
, поправочный коэффициент равен10
, и вы выполняете расчет:(Очень быстрое) решение выглядит примерно так:
В этом случае:
Я определенно рекомендую использовать проверенную библиотеку, такую как SinfulJS
источник
Вы только выполняете умножение? Если так, то вы можете использовать в своих интересах секрет секретной десятичной арифметики. Это так
NumberOfDecimals(X) + NumberOfDecimals(Y) = ExpectedNumberOfDecimals
. То есть, если у нас есть,0.123 * 0.12
то мы знаем, что будет 5 десятичных знаков, потому что0.123
имеет 3 десятичных знака и0.12
имеет два. Таким образом, если JavaScript дал нам число, подобное,0.014760000002
мы можем безопасно округлить до 5-го знака после запятой, не боясь потерять точность.источник
Вы ищете
sprintf
реализацию для JavaScript, чтобы вы могли выписывать числа с небольшими ошибками (поскольку они хранятся в двоичном формате) в ожидаемом формате.Попробуйте javascript-sprintf , вы бы назвали это так:
распечатать свой номер как число с двумя десятичными знаками.
Вы также можете использовать Number.toFixed () для отображения, если вы не хотите включать больше файлов просто для округления с плавающей запятой с заданной точностью.
источник
Я считаю, что BigNumber.js отвечает моим потребностям.
У него хорошая документация, и автор очень старательно реагирует на отзывы.
Этот же автор имеет 2 другие похожие библиотеки:
Big.js
и Decimal.js
Вот код, использующий BigNumber:
источник
---или---
---также---
--- как в ---
источник
Эта функция будет определять необходимую точность по умножению двух чисел с плавающей запятой и возвращать результат с соответствующей точностью. Элегантно, хотя это не так.
источник
Удивительно, но эта функция еще не была опубликована, хотя у других есть похожие варианты. Это из веб-документов MDN для Math.round (). Это сжато и допускает переменную точность.
console.log (precisionRound (1234.5678, 1)); // ожидаемый результат: 1234.6
console.log (precisionRound (1234.5678, -1)); // ожидаемый результат: 1230
ОБНОВЛЕНИЕ: 20 августа 2019 г. Только что заметил эту ошибку. Я полагаю, что это связано с ошибкой точности с плавающей точкой с Math.round ().
Эти условия работают правильно:
Fix:
Это просто добавляет цифру справа при округлении десятичных дробей. MDN обновил страницу Math.round, так что, возможно, кто-то может предложить лучшее решение.
источник
Вы просто должны решить, сколько десятичных цифр вы на самом деле хотите - не можете съесть торт и съесть его тоже :-)
Числовые ошибки накапливаются при каждой последующей операции, и если вы не отрежете ее раньше, она будет только расти. Числовые библиотеки, которые представляют результаты, которые выглядят чистыми, просто обрезают последние 2 цифры на каждом шаге, числовые сопроцессоры также имеют «нормальную» и «полную» длину по той же причине. Вырезание обходится дешево для процессора, но очень дорого для вас в сценарии (умножение, деление и использование pov (...)). Хорошая математическая библиотека предоставит слово (x, n), чтобы сделать отсечение для вас.
Поэтому, по крайней мере, вы должны сделать глобальную переменную / константу с помощью pov (10, n) - это означает, что вы определились с точностью, которая вам нужна :-) Затем выполните:
Вы также можете продолжать делать математику и только обрезать в конце - при условии, что вы только отображаете, а не делаете if-s с результатами. Если вы можете сделать это, тогда .toFixed (...) может быть более эффективным.
Если вы делаете сравнения if-s / и не хотите сокращать их, вам также нужна небольшая константа, обычно называемая eps, которая на один десятичный знак выше максимальной ожидаемой ошибки. Скажем, что ваш предел составляет последние два знака после запятой - тогда ваш eps имеет 1 на 3-м месте от последнего (3-е наименее значимое), и вы можете использовать его, чтобы сравнить, находится ли результат в пределах ожидаемого диапазона eps (0,02 -eps <0,1) * 0,2 <0,02 + EPS).
источник
Math.floor(-2.1)
есть-3
. Так что, возможно, используйте, например,Math[x<0?'ceil':'floor'](x*PREC_LIM)/PREC_LIM
floor
вместоround
?Вы можете использовать
parseFloat()
и,toFixed()
если вы хотите обойти эту проблему для небольшой операции:источник
Функция round () на phpjs.org работает хорошо: http://phpjs.org/functions/round
источник
0.6 * 3 это круто!)) Для меня это отлично работает
Очень очень просто))
источник
8.22e-8 * 1.3
?Обратите внимание, что для общего назначения такое поведение, вероятно, будет приемлемым.
Проблема возникает при сравнении этих значений с плавающей запятой для определения соответствующего действия.
С появлением ES6 определена новая константа,
Number.EPSILON
чтобы определить допустимый предел погрешности:вместо того, чтобы проводить сравнение, как это
Вы можете определить пользовательскую функцию сравнения, например так:
Источник: http://2ality.com/2015/04/numbers-math-es6.html#numberepsilon
источник
0.9 !== 0.8999999761581421
Результат, который вы получили, является правильным и достаточно согласованным для реализаций с плавающей запятой на разных языках, процессорах и операционных системах - единственное, что меняется, - это уровень погрешности, когда число с плавающей запятой на самом деле удваивается (или выше).
0,1 в двоичных числах с плавающей запятой - как 1/3 в десятичной (т. Е. 0,3333333333333 ... навсегда), точного способа справиться с этим просто нет.
Если вы имеете дело с числами с плавающей запятой, всегда ожидайте небольших ошибок округления, поэтому вам также всегда придется округлять отображаемый результат до чего-то разумного. Взамен вы получаете очень очень быструю и мощную арифметику, потому что все вычисления находятся в собственном двоичном файле процессора.
В большинстве случаев решение состоит не в том, чтобы переключаться на арифметику с фиксированной запятой, главным образом потому, что она намного медленнее и в 99% случаев вам просто не нужна точность. Если вы имеете дело с вещами, которым нужен такой уровень точности (например, финансовые транзакции), Javascript, вероятно, не лучший инструмент для использования в любом случае (так как вы хотите применять типы с фиксированной запятой, статический язык, вероятно, лучше ).
Вы ищете элегантное решение, тогда я боюсь, что это оно и есть: всплывающие подсказки быстрые, но с небольшими ошибками округления - всегда округляются до чего-то разумного при отображении результатов.
источник
Чтобы избежать этого, вы должны работать с целочисленными значениями, а не с плавающей точкой. Поэтому, когда вы хотите иметь точность в 2 позиции, работайте со значениями * 100, для 3 позиций используйте 1000. При отображении вы используете форматер, чтобы вставить разделитель.
Многие системы опускают работу с десятичными знаками таким образом. Вот почему многие системы работают с центами (как целые числа) вместо долларов / евро (как с плавающей запятой).
источник
проблема
Плавающая точка не может хранить все десятичные значения точно. Поэтому при использовании форматов с плавающей запятой всегда будут ошибки округления входных значений. Ошибки на входах курса приводят к ошибкам на выходе. В случае дискретной функции или оператора на выходе могут быть большие различия вокруг точки, где функция или оператор дискретны.
Ввод и вывод для значений с плавающей запятой
Таким образом, при использовании переменных с плавающей запятой вы всегда должны знать об этом. И любой вывод, который вы хотите получить из вычисления с плавающей запятой, всегда должен быть отформатирован / обработан перед отображением с учетом этого.
Когда используются только непрерывные функции и операторы, часто выполняется округление до желаемой точности (не усекайте). Стандартные функции форматирования, используемые для преобразования чисел с плавающей точкой, обычно делают это для вас.
Поскольку при округлении добавляется ошибка, которая может привести к тому, что общая ошибка составляет более половины требуемой точности, выходные данные следует корректировать на основе ожидаемой точности входных данных и требуемой точности выходных данных. Вам следует
Эти две вещи, как правило, не выполняются, и в большинстве случаев различия, вызванные их отсутствием, слишком малы, чтобы быть важными для большинства пользователей, но у меня уже был проект, в котором вывод не был принят пользователями без этих исправлений.
Дискретные функции или операторы (например, по модулю)
Когда задействованы дискретные операторы или функции, могут потребоваться дополнительные корректировки, чтобы убедиться, что выходные данные соответствуют ожидаемым. Округление и добавление небольших исправлений перед округлением не может решить проблему.
Может потребоваться специальная проверка / исправление промежуточных результатов расчета сразу после применения дискретной функции или оператора. Для конкретного случая (оператор по модулю ) см. Мой ответ на вопрос: почему оператор модуля возвращает дробное число в javascript?
Лучше избегать проблем
Часто более эффективно избежать этих проблем, используя типы данных (целочисленные или с фиксированной запятой) для таких вычислений, которые могут хранить ожидаемый ввод без ошибок округления. Примером этого является то, что вы никогда не должны использовать значения с плавающей запятой для финансовых расчетов.
источник
Посмотрите на арифметику с фиксированной точкой . Вероятно, это решит вашу проблему, если диапазон чисел, с которыми вы хотите работать, невелик (например, валюта). Я бы округлил его до нескольких десятичных значений, что является самым простым решением.
источник
Попробуйте мою chiliadic арифметическую библиотеку, которую вы можете увидеть здесь . Если вы хотите более позднюю версию, я могу получить ее.
источник
Вы не можете представлять большинство десятичных дробей точно с двоичными типами с плавающей запятой (это то, что ECMAScript использует для представления значений с плавающей запятой). Так что элегантного решения не существует, если вы не используете арифметические типы произвольной точности или тип с плавающей запятой в десятичной форме. Например, приложение Calculator, поставляемое с Windows, теперь использует произвольную арифметику точности для решения этой проблемы .
источник
источник
Вы правы, причина этого - ограниченная точность чисел с плавающей запятой. Храните свои рациональные числа как деление двух целых чисел, и в большинстве случаев вы сможете хранить числа без потери точности. Когда дело доходит до печати, вы можете отобразить результат в виде дроби. С представлением я предложил, это становится тривиальным.
Конечно, это не очень поможет с иррациональными числами. Но вы можете оптимизировать свои вычисления таким образом, чтобы они вызывали наименьшую проблему (например, обнаружение таких ситуаций, как
sqrt(3)^2)
.источник
<pedant>
самом деле, операционная система определила это, чтобы неточные операции с плавающей запятой, что неправильно</pedant>
У меня была проблема с ошибками округления с модом 3. Иногда, когда я должен был получить 0, я получал .000 ... 01. Это достаточно просто для обработки, просто проверьте на <= .01. Но тогда иногда я получал 2.99999999999998. ОЙ!
BigNumbers решили проблему, но представили другую, несколько ироническую проблему. Когда я пытался загрузить 8.5 в BigNumbers, мне сообщили, что это действительно 8.4999… и в нем более 15 значащих цифр. Это означало, что BigNumbers не могли принять это (я думаю, я упоминал, что эта проблема была несколько ироничной).
Простое решение иронической проблемы:
источник
Используйте номер (1.234443). ToFixed (2); это напечатает 1.23
источник
decimal.js , big.js или bignumber.js могут быть использованы, чтобы избежать проблем манипуляции с плавающей точкой в Javascript:
ссылка на подробные сравнения
источник
Элегантный, предсказуемый и многоразовый
Давайте разберемся с проблемой элегантным способом многократного использования. Следующие семь строк позволят вам получить желаемую точность с плавающей запятой для любого числа, просто добавив
.decimal
в конец числа, формулу или встроеннуюMath
функцию.Ура!
источник
использование
источник
Math.round(x*Math.pow(10,4))/Math.pow(10,4);
. Округление всегда вариант, но я просто хотел узнать, есть ли какое-нибудь лучшее решениене элегантно, но делает работу (удаляет завершающие нули)
источник
Это работает для меня:
источник
Вывод с использованием следующей функции:
Обратите внимание на вывод
toFixedCurrency(x)
.источник