JS: точность вычислений с плавающей точкой

Статус
Закрыто для дальнейших ответов.

антиКИЛЛЕР

Well-Known Member
Регистрация
28.07.2011
Сообщения
92
Итак, у меня следующая ситуация:
на странице есть n-ое количество форм (может быть и штук 50). В каждой из этих форм есть по 8 вычисляемых полей и по 11 вводимых пользователями полей и выводимых из БД. Все вводимые пользователями поля и выводимые из БД поля имеют точность в 9 знаков после запятой, а все вычисляемые поля должны иметь точность в 2 знака после запятой.
Поля вводимые пользователями используются для рассчёта других 7 полей. При этом все поля взаимосвязаны между собой и изменения в одном из них, влияют на значения в во всех других.
При этом есть ещё общая форма с коэффициентами, где каждое из полей влияет на все остальные.

Так вот, проблема в том, что эти 7 полей - считаются деньги. И точность нужна +-1 копейка.
Алгоритм вычислений таков, что нужно всё считать как с можно большей точностью, а потом только, на конечном этапе округлять до двух знаков (если округлять сразу, не совсем верные цифры получаются).
А особенность JS вычислений, как раз в неточности. Ошибка 0.0000000000000001 - это нормально, при округлении до 2-х знаков она строется. Но особенности алгоритма таковы, что в данном случае ошибка такая может накапливаться и влиять на результат впоть до 10 копеек, что недопустимо.
В прицнипе, логично в таком случае передавать данные на сервер, там уже считать, и выдавать в js обратно. Но тут проблема в том, что полей таких много, и вычислять итоговые суммы пользователям нужно "на лету", по onkeyup, ну или по onchange как минимум. Передавать каждый раз такой обьём данных (в самом лучшем случае это будет 70 полей с цифрами) на сервер весьма накладно, да и не хочется. (всё равно придётся когда пользователи сохраняют, но сохраняют они один раз, а чтобы цифры подобрать нужные под нужную отчётность и требуемые показатели, менять значения они могут по 50 раз перед сохранением).

Так вот, какие кто знает способы повышения точности, может есть какие то допольнительные библиотеки, устраняющие эти ошибки при вычислениях или ещё что?
Очень бы хотелось услышать о вариантах решения этой проблемы. Вариант с передачей аяксом довольно сильно тормозит и куча запросов от 2-3 пользователей могут сделать плохо серверу, а заставлять пользователей вводить всё что хотят а потом жать на кнопку "произвести рассчёт" не очень хочется.

Сейчас вижу вариант сделать вычисления аяксом по onkeyup, но при этом сделать так, чтобы отправка аякс запроса происходила не чаще чем раз в 1.5-2 секунды.
Но может есть способы заставить JS считать точно? Может кто сталкивался, знает?
 

BaNru

Пацифизжу
Команда форума
Регистрация
13.11.2010
Сообщения
4 138
Алгоритм вычислений таков, что нужно всё считать как с можно большей точностью, а потом только, на конечном этапе округлять до двух знаков (если округлять сразу, не совсем верные цифры получаются). А особенность JS вычислений, как раз в неточности. Ошибка 0.0000000000000001 - это нормально, при округлении до 2-х знаков она строется. Но особенности алгоритма таковы, что в данном случае ошибка такая может накапливаться и влиять на результат впоть до 10 копеек, что недопустимо.
Все не прочитал, слишком уж много буковок.
Но не проще ли в финальном результате еще раз считать все формы, не округленные, а только потом округлять?
 

антиКИЛЛЕР

Well-Known Member
Регистрация
28.07.2011
Сообщения
92
[member=BaNru],
Так как раз в этом и проблема.
При рассчёте не округленных значений (расчтё по каждому из чисел состоит из нескольких умножений, сложений и делений) ошибка может достигать в общей сложности 0.06 и на финальном этапе, когда происходит округление появляется ошибка. Т.е. например если вручную на калькуляторе посчитать по формуле надбавку при входных данных
8.3482835
5.98027475
8.38492935
на выходе получим 11.9377846252 округлив - 11.94
а при счёте тех же неокруглённых значений в js получим 11.95989473 и при округлении получаем 11.96
Конечно я привер простой пример, в реальности конечные числа считаются из 5+ входных цифр и алгоритмы там сложные (формулу не скажу, всё на работе лежит)
 

Aristotel

Well-Known Member
Регистрация
14.04.2011
Сообщения
213
Мне кажется что причина не в javascript а в вшем алгоритме. Я не думаю что javascript считает хуже калькулятора. На сколько я помню экран калькулятора ограничен в размере, цифры в алгоритме будут гораздо длиннее и отсюда конечное число в вашем случае больше чем на калькуляторе (зависит от формулы).
Возможно гдето в алгаритмах, какиета числа крамсаются, плюс вы сказали что все числа связаны, как я понимаю, при нажатие на кнопку сразу все данные меняются. Возможна чтота не успевает поменятся из за сложнасти алгорится и из за скорости набора.
Мне кажется есть много очень причин каторые стоит проверить. И стоит почитсять мануалы на тему javascript и float.
 

антиКИЛЛЕР

Well-Known Member
Регистрация
28.07.2011
Сообщения
92
[member=Aristotel],Нет. проблема не в алгоритме, а в самом JS. Там в качестве типа данных используется 8-ми байтный float64, который не гарантирует точность вычислений (sic!).
Собственно попробуйте запустить нечто вроде "alert(0.1*0.4);" и посмотрите результат. (и это не баг, это фича!)
Я уже не раз с таким поведением сталкивался, но раньше это в конечном итоге не приводило к проблемам.
Уж не знаю почему в js так, я понимаю что различные таймеры могут выполняться не в то время, на которое они назначены т.к. ресурсы заняты, мне кажется что арифметические операции всегда должны быть четкими и правильными.

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

BaNru

Пацифизжу
Команда форума
Регистрация
13.11.2010
Сообщения
4 138
Умножаем все на 10 в N степени
Все суммируем
Делим на эту же 10 в N степени
Округляем
:biggrin:
 

medwoodu

Злобный модер
Регистрация
22.12.2005
Сообщения
1 418
Хех, а увеличить порядок данных? Например домножить все на сто, а потом результат на 100 поделить?
 

антиКИЛЛЕР

Well-Known Member
Регистрация
28.07.2011
Сообщения
92
[member=BaNru],[member=Medwoodu], Да, спасибо. До этого я как то и не додумался. Вполне себе решение.

Хотя я так же нашёл на просторах сети аналог библиотеки bcmath для js: http://sourceforge.net/projects/bcmath-js
Она правда не идеальна (как по мне так и не очень удобно, разделить/умножить возможно проще будет) так же, но вполне себе может быть альтернативой.
 
Статус
Закрыто для дальнейших ответов.
Верх Низ