| | |
JavaScript decimal arithmetic
Please support our JavaScript / DHTML / AJAX advertiser: PostgreSQL or MySQL? Compare and contrast the two most popular open source databases
![]() |
•
•
Join Date: Sep 2009
Posts: 5
Reputation:
Solved Threads: 0
I am aware that JavaScript arithmetic does not handle either really large numbers or decimals well. This is why I have made this library of functions. I am posting it here rather than under "code snippets" because I would like to learn from your comments.
Along with the library is a demo.
Along with the library is a demo.
JavaScript / DHTML / AJAX Syntax (Toggle Plain Text)
<html> <head> </head> <body> <script language="JavaScript"> <!-- // // JavaScript bignum / big decimal library // by Robert Lozyniak - r07271368@hotmail.com // this is version number 2 // this version 10 Sep 2009 at 07:47 UT // generic polynomial functions // here, we represent polynomials as arrays // with the array indices being the coefficients // // it is expected that there be no gaps in these arrays! // (i.e. there is no checking for missing elements or NaNs) function figz(x,n) { if (n<0 || n>=x.length) return 0; return x[n]-0; } function polyAdd() { // polynomial addition var i=0; var j=0; var r=new Array(); for (i=0; i<arguments.length; i++) { for (j=0; j<arguments[i].length; j++) { r[j]=figz(r,j)+figz(arguments[i],j); } } return r; } function polyMul(x,y) { // polynomial multiplication var i=0; var j=0; var m; var r=new Array(); for (i=0; i<x.length+y.length-1; i++) r[i]=0; for (i=0; i<x.length; i++) { m=figz(x,i); if (m!=0) { // why bother if m==0 ? for (j=0; j<y.length; j++) { r[i+j]+=(m*figz(y,j)); } } } return r; } function polyMulSc(x,n) { // polynomial multiplication by scalar var j=0; var r=new Array(); for (j=0; j<x.length; j++) { r[j]=x[j]*n; } return r; } function polyComp(x,y) { // polynomial comparison var i; var ml=x.length; if (y.length>ml) ml=y.length; for (i=ml-1; i>=0; i--) { if (figz(x,i)!=figz(y,i)) return figz(x,i)-figz(y,i); } return 0; } function polyLeftShift(x,n) { // polynomial left shift var j=0; var r=new Array(); for (j=0; j<x.length+n; j++) { r[j]=figz(x,j-n); } return r; } // now, on to chiliadic (base 1000) numbers and arithmetic // define some useful "constants" var CHIL_MINUS_ONE=new Array(); CHIL_MINUS_ONE[0]=0-1; var CHIL_ZERO=new Array(); // zero is an empty array var CHIL_ONE=new Array(); CHIL_ONE[0]=1; var CHIL_TWO=new Array(); CHIL_TWO[0]=2; var CHIL_TEN=new Array(); CHIL_TEN[0]=10; var CHIL_ONE_HUNDRED=new Array(); CHIL_ONE_HUNDRED[0]=100; var CHIL_ONE_THOUSAND=new Array(); CHIL_ONE_THOUSAND[0]=0; CHIL_ONE_THOUSAND[1]=1; function chilNorm(x) { // returns normalized form of chiliadic integer // a function such as this one is at the heart of all base-n arithmetic // here, the base is one thousand var i=0; var n=0; var w=0; var carry=0; var r=new Array(); while (i<x.length || carry!=0) { n=carry+figz(x,i); carry=0; w=n%1000; // no negative digits except possibly most significant digit if (w<0) w+=1000; carry=Math.round((n-w)*0.001); if (i>=x.length && carry==0-1 && w>0) { w-=1000; carry=0; } r[i]=w; i++; } i=r.length-1; // shorten length as much as possible without changing numeric value while(i>0-1 && figz(r,i) == 0) i-- ; while(i>0 && figz(r,i) == 0-1 && figz(r,i-1) > 0) { i--; r[i]+=n*1000; } if (i!=r.length-1) { rr=new Array(); for (n=0; n<=i; n++) rr[n]=r[n]; return rr; } return r; } // base-n arithmetic is just polynomial arithmetic with carries function chilComp(x,y) { // compares two chiliadic integers return polyComp(chilNorm(x),chilNorm(y)); } function chilIsZero(x) { // does this chiliadic integer equal zero? return (chilNorm(x).length==0); } function chilIsPos(x) { // is this chiliadic integer positive? var xn=chilNorm(x); return (xn.length>0 && figz(xn,xn.length-1)>0); } function chilIsNeg(x) { // is this chiliadic integer negative? var xn=chilNorm(x); return (xn.length>0 && figz(xn,xn.length-1)<0); } function chilAdd() { // adds chiliadic integers var t = polyAdd.apply(null, arguments); return chilNorm(t); } function chilNeg(x) { // negates a chiliadic integer return chilNorm(polyMulSc(x,0-1)); } function chilSub(x,y) { // subtracts two chiliadic integers return chilNorm(polyAdd(x,polyMulSc(y,0-1))); } function chilMul(x,y) { // multiplies two chiliadic integers return chilNorm(polyMul(x,y)); } function chilMulSc(x,n) { // multiplies a chiliadic integer by a scalar return chilNorm(polyMulSc(x,n)); } // floats are useful for approximations // and approximations can be refined by other means function chilMantFloat(x) { // takes chiliadic integer ... // hard to explain; helper for at least one other function var r = 0; r += figz(x,x.length-7)/1e18; r += figz(x,x.length-6)/1e15; r += figz(x,x.length-5)/1e12; r += figz(x,x.length-4)/1e9; r += figz(x,x.length-3)/1e6; r += figz(x,x.length-2)/1e3; r += figz(x,x.length-1)/1e0; return r; } function scaledFloatToChil (x,s) { // x = float, s = scale // x is JavaScript float, s = scale as JavaScript numeric // returns chiliadic integer close to (x*(1000**s)) var r = new Array(); var xa; var xb = x; var i; for (i=0; i<7; i++) { xa = Math.round(xb); xb -= xa; r[i] = xa; xb *= 1000; } r = r.reverse(); r = polyLeftShift(r,s-(r.length-1)); return chilNorm(r); } function chilAbs(x) { // absolute value of chiliadic integer if (chilIsNeg(x)) return chilNeg(x); return chilNorm(x); } function chilDiv(x,y) { // divides two chiliadic integers if (chilIsZero(y)) return false; if (chilIsNeg(y)) return chilDiv(chilNeg(x),chilNeg(y)); // so if we're here, y must be positive // this simplifies the rounding if (chilIsZero(x)) return new Array(); var qq; var r = new Array(); var ymf = chilMantFloat(y); var xmf; var xres = chilNeg(x); // because easier to add than subtract // I could make this recursive but I don't think I should while (chilComp(chilAbs(xres),y)>=0) { xmf = chilMantFloat(xres); qq = scaledFloatToChil (0-(xmf/ymf),xres.length-y.length); r = polyAdd(r,qq); xres = chilAdd(xres,polyMul(qq,y)); } if (chilIsPos(xres)) { // in case we overshot our quotient r = polyAdd(r,CHIL_MINUS_ONE); } return chilNorm(r); } function strToChil (s) { // converts string to chiliadic integer // I found this regex on the net var isInteger_re = /^\s*(\+|-)?\d+\s*$/; if ( String(s).search (isInteger_re) == -1) return false; var ssa = s.substring(0,1); var ssb = s.substring(1,s.length); if (ssa!="+" && ssa!="-") { ssb = ssa+ssb; ssa = "+"; }; ssb = "00"+ssb; ssb = ssb.substring(ssb.length%3,ssb.length); var r = new Array(); var i; for (i=0; i*3<ssb.length; i++) { r[i]=parseInt(ssb.substring(i*3,i*3+3),10); } r = r.reverse(); return (ssa=="+"?chilNorm(r):chilNeg(r)); } function chilToStr (x) { // converts chiliadic integer to string if (chilIsZero(x)) return "0"; // must special-case this if (chilIsNeg(x)) return "-"+chilToStr(chilNeg(x)); var xd=chilNorm(x).reverse(); var r=""+xd[0]; var i; var s; for (i=1; i<xd.length; i++) { // i should start as 1; we already have 0 s="00"+xd[i]; r+=s.substring(s.length-3,s.length); } return r; } function chilPowerOfTen (n) { // returns e.g. [0,100] for 5 if (n%3==0) return polyLeftShift(CHIL_ONE,Math.round(n/3)); if (n%3==1) return polyLeftShift(CHIL_TEN,Math.round(n/3)); if (n%3==2) return polyLeftShift(CHIL_ONE_HUNDRED,Math.round(n/3)-1); } function strToDec(s) { // converts string decimal to scaled integer form // output is a two-element array // element 0 is scale as an ordinary number // element 1 is a chiliadic integer // example: 3.1416 => [4,[416,31]] // // this regex is from the Internet var isDecimal_re = /^\s*(\+|-)?((\d+(\.\d+)?)|(\.\d+))\s*$/; if ( String(s).search (isDecimal_re) == -1) return false; var r=new Array(); var dpi=s.indexOf("."); var ss=(s+((dpi==0-1)?".":"")).split("."); r[0]=ss[1].length; r[1]=strToChil(ss.join("")); return r; } function decToStr(x) { // inverse of strToDec // for decimal fractions less than 1, leading zero is used var sc=x[0]; var mant=x[1]; if (sc==0) return chilToStr(mant); var r=new Array(); if(chilIsNeg(mant)) { r[0]=sc; r[1]=chilNeg(mant); return "-"+decToStr(r); } // this is really inefficient but I will use it anyway var p=chilPowerOfTen(sc); var ri=chilDiv(mant,p); r[0]=chilToStr(ri); r[1]=chilToStr(chilAdd(p,chilSub(mant,chilMul(ri,p)))); r[1]=r[1].substring(1,r[1].length); return r.join("."); } function decScaleTo(x,s) { // changes x to scale s // where x is a decimal as a scaled chiliadic integer // for ease of reuse, all rounding is toward negative infinity if (s==x[0]) return x; r=new Array(); if (s>x[0]) { r[0] = s; r[1] = chilMul(x[1],chilPowerOfTen(s-x[0])); return r; } // if we're here, then s<x[0] r[0]=s; r[1]=chilDiv(x[1],chilPowerOfTen(x[0]-s)); return r; } function decAdd() { // adds decimals as scaled chiliadic integers var i; var smax = 0; for (i=0; i<arguments.length; i++) { if (smax<arguments[i][0]) smax = arguments[i][0]; } var mnta = new Array(); for (i=0; i<arguments.length; i++) { mnta[i] = decScaleTo(arguments[i],smax)[1]; } var mnts = chilAdd.apply(null, mnta); var r = new Array(); r[0] = smax; r[1] = mnts; return r; } function decNeg(x) { // negates a scaled chiliadic integer var r=new Array(); r[0] = x[0]; r[1] = chilNeg(x[1]); return r; } function decSub(x,y) { // subtracts two scaled chiliadic integers return decAdd(x,decNeg(y)); } function decIsZero(x) { // tests for zero return chilIsZero(x[1]); } function decIsPos(x) { // tests for positive return chilIsPos(x[1]); } function decIsNeg(x) { // tests for negative return chilIsNeg(x[1]); } function decComp(x,y) { // compares two scaled chiliadic integers // method: compare mantissa of (x-y) with 0 return chilComp(decSub(x,y)[1],new Array()); } function decMul(x,y) { // multiplies two scaled chiliadic integers var r = new Array(); r[0] = x[0] + y[0]; r[1] = chilMul(x[1],y[1]); return r; } function decDiv(x,y,s) { // divides x by y with scale s // x and y are scaled chiliadic integers var r = new Array(); r[0] = s; r[1] = chilDiv(decScaleTo(x,y[0]+s)[1],y[1]); if (r[1]===false) return false; return r; } // library ends here; now we have the functions specific to this form function doTheMath() { var n1s = document.calcForm.n1.value; var n2s = document.calcForm.n2.value; var n1d = strToDec(n1s); var n2d = strToDec(n2s); var opnum = 0; var outtext = "Hmmm... if I've done the math right...\n"; while (document.calcForm.mathradio[opnum].checked == false) opnum++; if (n1d===false || n2d===false) { alert("I need two valid numbers.\nDigits and decimal points only.\nNegative numbers are OK too."); } else if ((opnum==3 || opnum==4) && decIsZero(n2d)) { alert("Can't divide by zero.") } else if (opnum==0) { outtext += n1s +" plus "+ n2s +" equals "+decToStr(decAdd(n1d,n2d)); alert(outtext); } else if (opnum==1) { outtext += n1s +" minus "+ n2s +" equals "+decToStr(decSub(n1d,n2d)); alert(outtext); } else if (opnum==2) { outtext += n1s +" times "+ n2s +" equals "+decToStr(decMul(n1d,n2d)); alert(outtext); } else if (opnum==3) { outtext += n1s +" divided by "+ n2s +" equals "+decToStr(decDiv(n1d,n2d,2)); outtext += "\n(to 2 decimal places)" alert(outtext); } else if (opnum==4) { outtext += n1s +" divided by "+ n2s +" equals "+decToStr(decDiv(n1d,n2d,20)); outtext += "\n(to 20 decimal places)" alert(outtext); } } // --> </script> <BODY> <FORM NAME="calcForm" ACTION="" METHOD="GET"> Give me a number: <BR> <INPUT TYPE="text" NAME="n1" SIZE="50" VALUE=""><BR> Give me another number: <BR> <INPUT TYPE="text" NAME="n2" SIZE="50" VALUE=""><BR> What to do with these numbers?<BR> <INPUT TYPE="radio" NAME="mathradio" VALUE="add" checked> Add them<BR> <INPUT TYPE="radio" NAME="mathradio" VALUE="sub"> Subtract them<BR> <INPUT TYPE="radio" NAME="mathradio" VALUE="mul"> Multiply them<BR> <INPUT TYPE="radio" NAME="mathradio" VALUE="div2"> Divide them (2 decimal places)<BR> <INPUT TYPE="radio" NAME="mathradio" VALUE="div20"> Divide them (20 decimal places)<BR> <INPUT TYPE="button" NAME="mathbutton" Value="Do the math" onClick="doTheMath()"> </FORM> <br><br> This page and arithmetic library by Robert Lozyniak on 10 Sep 2009<br> e-mail: letter arr zero seven deuce seven one trey halfdozen eight at hotmail dot you know the rest </body></html>
![]() |
Similar Threads
- float arithmetic and decimal point precision (C++)
- Help with adding 2 arrays of different sizes (C++)
- need help with 16 - bit calculator (Assembly)
- Quick Question: Is J# the same thing as Java? (Java)
- hop over decimal and straight to business (C++)
- setting the number of decimal points in double varibale (C)
- 128bit arithmetic (C++)
- Decimal Problem In C (C)
Other Threads in the JavaScript / DHTML / AJAX Forum
- Previous Thread: Ajax executing more than once when its not ment to!!!!!
- Next Thread: Cant able to show jquery confirm box using scriptmanager
Views: 1088 | Replies: 0
| Thread Tools | Search this Thread |
Tag cloud for decimal, javascript, math
2.0 adobe adobeacrobat ajax ajaxcode ajaxhelp algorithm angle api array asp bitmap box c# c++ cart checkbox close codeinjection coldfusion column conversion createrange() decimal degrees development dom download drawing element email enum epilepsy equation external firefox flash form frameworks function gcd gdi+ google grade griefers gwt hackers html htmlform iframe images java javascript jquery jsf jsp loop mandelbrot maps masterpage math mathematics microsoft mp4 mysql news operator pdf php picturebox post prime primenumbersinrange problem radians recursion recursive runtime safari search select session shopping size statistics tables technology text textbox treeview unicode validation vbnet w3c web windowofwords windowsxp wolframalpha xml zeroday






