943,772 Members | Top Members by Rank

Ad:
Sep 10th, 2009
0

JavaScript decimal arithmetic

Expand Post »
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.

JavaScript / DHTML / AJAX Syntax (Toggle Plain Text)
  1.  
  2.  
  3. <html>
  4. <head>
  5.  
  6. </head>
  7. <body>
  8.  
  9.  
  10. <script language="JavaScript">
  11. <!-- //
  12.  
  13. // JavaScript bignum / big decimal library
  14. // by Robert Lozyniak - r07271368@hotmail.com
  15. // this is version number 2
  16. // this version 10 Sep 2009 at 07:47 UT
  17.  
  18.  
  19. // generic polynomial functions
  20. // here, we represent polynomials as arrays
  21. // with the array indices being the coefficients
  22. //
  23. // it is expected that there be no gaps in these arrays!
  24. // (i.e. there is no checking for missing elements or NaNs)
  25.  
  26. function figz(x,n) {
  27. if (n<0 || n>=x.length) return 0;
  28. return x[n]-0;
  29. }
  30.  
  31. function polyAdd() { // polynomial addition
  32. var i=0;
  33. var j=0;
  34. var r=new Array();
  35. for (i=0; i<arguments.length; i++) {
  36. for (j=0; j<arguments[i].length; j++) {
  37. r[j]=figz(r,j)+figz(arguments[i],j);
  38. }
  39. }
  40. return r;
  41. }
  42.  
  43. function polyMul(x,y) { // polynomial multiplication
  44. var i=0;
  45. var j=0;
  46. var m;
  47. var r=new Array();
  48. for (i=0; i<x.length+y.length-1; i++) r[i]=0;
  49. for (i=0; i<x.length; i++) {
  50. m=figz(x,i);
  51. if (m!=0) { // why bother if m==0 ?
  52. for (j=0; j<y.length; j++) {
  53. r[i+j]+=(m*figz(y,j));
  54. }
  55. }
  56. }
  57. return r;
  58. }
  59.  
  60. function polyMulSc(x,n) { // polynomial multiplication by scalar
  61. var j=0;
  62. var r=new Array();
  63. for (j=0; j<x.length; j++) {
  64. r[j]=x[j]*n;
  65. }
  66. return r;
  67. }
  68.  
  69. function polyComp(x,y) { // polynomial comparison
  70. var i;
  71. var ml=x.length;
  72. if (y.length>ml) ml=y.length;
  73. for (i=ml-1; i>=0; i--) {
  74. if (figz(x,i)!=figz(y,i)) return figz(x,i)-figz(y,i);
  75. }
  76. return 0;
  77. }
  78.  
  79. function polyLeftShift(x,n) { // polynomial left shift
  80. var j=0;
  81. var r=new Array();
  82. for (j=0; j<x.length+n; j++) {
  83. r[j]=figz(x,j-n);
  84. }
  85. return r;
  86. }
  87.  
  88.  
  89. // now, on to chiliadic (base 1000) numbers and arithmetic
  90.  
  91. // define some useful "constants"
  92.  
  93. var CHIL_MINUS_ONE=new Array();
  94. CHIL_MINUS_ONE[0]=0-1;
  95.  
  96. var CHIL_ZERO=new Array(); // zero is an empty array
  97.  
  98. var CHIL_ONE=new Array();
  99. CHIL_ONE[0]=1;
  100.  
  101. var CHIL_TWO=new Array();
  102. CHIL_TWO[0]=2;
  103.  
  104. var CHIL_TEN=new Array();
  105. CHIL_TEN[0]=10;
  106.  
  107. var CHIL_ONE_HUNDRED=new Array();
  108. CHIL_ONE_HUNDRED[0]=100;
  109.  
  110. var CHIL_ONE_THOUSAND=new Array();
  111. CHIL_ONE_THOUSAND[0]=0;
  112. CHIL_ONE_THOUSAND[1]=1;
  113.  
  114.  
  115. function chilNorm(x) { // returns normalized form of chiliadic integer
  116. // a function such as this one is at the heart of all base-n arithmetic
  117. // here, the base is one thousand
  118. var i=0;
  119. var n=0;
  120. var w=0;
  121. var carry=0;
  122. var r=new Array();
  123. while (i<x.length || carry!=0) {
  124. n=carry+figz(x,i);
  125. carry=0;
  126. w=n%1000;
  127. // no negative digits except possibly most significant digit
  128. if (w<0) w+=1000;
  129. carry=Math.round((n-w)*0.001);
  130. if (i>=x.length && carry==0-1 && w>0) {
  131. w-=1000; carry=0;
  132. }
  133. r[i]=w;
  134. i++;
  135. }
  136. i=r.length-1;
  137. // shorten length as much as possible without changing numeric value
  138. while(i>0-1 && figz(r,i) == 0) i-- ;
  139. while(i>0 && figz(r,i) == 0-1 && figz(r,i-1) > 0) {
  140. i--;
  141. r[i]+=n*1000;
  142. }
  143. if (i!=r.length-1) {
  144. rr=new Array();
  145. for (n=0; n<=i; n++) rr[n]=r[n];
  146. return rr;
  147. }
  148. return r;
  149. }
  150.  
  151.  
  152. // base-n arithmetic is just polynomial arithmetic with carries
  153.  
  154. function chilComp(x,y) { // compares two chiliadic integers
  155. return polyComp(chilNorm(x),chilNorm(y));
  156. }
  157.  
  158. function chilIsZero(x) { // does this chiliadic integer equal zero?
  159. return (chilNorm(x).length==0);
  160. }
  161.  
  162. function chilIsPos(x) { // is this chiliadic integer positive?
  163. var xn=chilNorm(x);
  164. return (xn.length>0 && figz(xn,xn.length-1)>0);
  165. }
  166.  
  167. function chilIsNeg(x) { // is this chiliadic integer negative?
  168. var xn=chilNorm(x);
  169. return (xn.length>0 && figz(xn,xn.length-1)<0);
  170. }
  171.  
  172. function chilAdd() { // adds chiliadic integers
  173. var t = polyAdd.apply(null, arguments);
  174. return chilNorm(t);
  175. }
  176.  
  177. function chilNeg(x) { // negates a chiliadic integer
  178. return chilNorm(polyMulSc(x,0-1));
  179. }
  180.  
  181. function chilSub(x,y) { // subtracts two chiliadic integers
  182. return chilNorm(polyAdd(x,polyMulSc(y,0-1)));
  183. }
  184.  
  185. function chilMul(x,y) { // multiplies two chiliadic integers
  186. return chilNorm(polyMul(x,y));
  187. }
  188.  
  189. function chilMulSc(x,n) { // multiplies a chiliadic integer by a scalar
  190. return chilNorm(polyMulSc(x,n));
  191. }
  192.  
  193. // floats are useful for approximations
  194. // and approximations can be refined by other means
  195.  
  196. function chilMantFloat(x) { // takes chiliadic integer ...
  197. // hard to explain; helper for at least one other function
  198. var r = 0;
  199. r += figz(x,x.length-7)/1e18;
  200. r += figz(x,x.length-6)/1e15;
  201. r += figz(x,x.length-5)/1e12;
  202. r += figz(x,x.length-4)/1e9;
  203. r += figz(x,x.length-3)/1e6;
  204. r += figz(x,x.length-2)/1e3;
  205. r += figz(x,x.length-1)/1e0;
  206. return r;
  207. }
  208.  
  209. function scaledFloatToChil (x,s) { // x = float, s = scale
  210. // x is JavaScript float, s = scale as JavaScript numeric
  211. // returns chiliadic integer close to (x*(1000**s))
  212. var r = new Array();
  213. var xa;
  214. var xb = x;
  215. var i;
  216. for (i=0; i<7; i++) {
  217. xa = Math.round(xb);
  218. xb -= xa;
  219. r[i] = xa;
  220. xb *= 1000;
  221. }
  222. r = r.reverse();
  223. r = polyLeftShift(r,s-(r.length-1));
  224. return chilNorm(r);
  225. }
  226.  
  227.  
  228. function chilAbs(x) { // absolute value of chiliadic integer
  229. if (chilIsNeg(x)) return chilNeg(x);
  230. return chilNorm(x);
  231. }
  232.  
  233. function chilDiv(x,y) { // divides two chiliadic integers
  234. if (chilIsZero(y)) return false;
  235. if (chilIsNeg(y)) return chilDiv(chilNeg(x),chilNeg(y));
  236. // so if we're here, y must be positive
  237. // this simplifies the rounding
  238. if (chilIsZero(x)) return new Array();
  239. var qq;
  240. var r = new Array();
  241. var ymf = chilMantFloat(y);
  242. var xmf;
  243. var xres = chilNeg(x); // because easier to add than subtract
  244. // I could make this recursive but I don't think I should
  245. while (chilComp(chilAbs(xres),y)>=0) {
  246. xmf = chilMantFloat(xres);
  247. qq = scaledFloatToChil (0-(xmf/ymf),xres.length-y.length);
  248. r = polyAdd(r,qq);
  249. xres = chilAdd(xres,polyMul(qq,y));
  250. }
  251. if (chilIsPos(xres)) { // in case we overshot our quotient
  252. r = polyAdd(r,CHIL_MINUS_ONE);
  253. }
  254. return chilNorm(r);
  255. }
  256.  
  257. function strToChil (s) { // converts string to chiliadic integer
  258. // I found this regex on the net
  259. var isInteger_re = /^\s*(\+|-)?\d+\s*$/;
  260. if ( String(s).search (isInteger_re) == -1) return false;
  261. var ssa = s.substring(0,1);
  262. var ssb = s.substring(1,s.length);
  263. if (ssa!="+" && ssa!="-") {
  264. ssb = ssa+ssb;
  265. ssa = "+";
  266. };
  267. ssb = "00"+ssb;
  268. ssb = ssb.substring(ssb.length%3,ssb.length);
  269. var r = new Array();
  270. var i;
  271. for (i=0; i*3<ssb.length; i++) {
  272. r[i]=parseInt(ssb.substring(i*3,i*3+3),10);
  273. }
  274. r = r.reverse();
  275. return (ssa=="+"?chilNorm(r):chilNeg(r));
  276. }
  277.  
  278. function chilToStr (x) { // converts chiliadic integer to string
  279. if (chilIsZero(x)) return "0"; // must special-case this
  280. if (chilIsNeg(x)) return "-"+chilToStr(chilNeg(x));
  281. var xd=chilNorm(x).reverse();
  282. var r=""+xd[0];
  283. var i;
  284. var s;
  285. for (i=1; i<xd.length; i++) { // i should start as 1; we already have 0
  286. s="00"+xd[i];
  287. r+=s.substring(s.length-3,s.length);
  288. }
  289. return r;
  290. }
  291.  
  292. function chilPowerOfTen (n) { // returns e.g. [0,100] for 5
  293. if (n%3==0) return polyLeftShift(CHIL_ONE,Math.round(n/3));
  294. if (n%3==1) return polyLeftShift(CHIL_TEN,Math.round(n/3));
  295. if (n%3==2) return polyLeftShift(CHIL_ONE_HUNDRED,Math.round(n/3)-1);
  296. }
  297.  
  298. function strToDec(s) { // converts string decimal to scaled integer form
  299. // output is a two-element array
  300. // element 0 is scale as an ordinary number
  301. // element 1 is a chiliadic integer
  302. // example: 3.1416 => [4,[416,31]]
  303. //
  304. // this regex is from the Internet
  305. var isDecimal_re = /^\s*(\+|-)?((\d+(\.\d+)?)|(\.\d+))\s*$/;
  306. if ( String(s).search (isDecimal_re) == -1) return false;
  307. var r=new Array();
  308. var dpi=s.indexOf(".");
  309. var ss=(s+((dpi==0-1)?".":"")).split(".");
  310. r[0]=ss[1].length;
  311. r[1]=strToChil(ss.join(""));
  312. return r;
  313. }
  314.  
  315. function decToStr(x) { // inverse of strToDec
  316. // for decimal fractions less than 1, leading zero is used
  317. var sc=x[0];
  318. var mant=x[1];
  319. if (sc==0) return chilToStr(mant);
  320. var r=new Array();
  321. if(chilIsNeg(mant)) {
  322. r[0]=sc;
  323. r[1]=chilNeg(mant);
  324. return "-"+decToStr(r);
  325. }
  326. // this is really inefficient but I will use it anyway
  327. var p=chilPowerOfTen(sc);
  328. var ri=chilDiv(mant,p);
  329. r[0]=chilToStr(ri);
  330. r[1]=chilToStr(chilAdd(p,chilSub(mant,chilMul(ri,p))));
  331. r[1]=r[1].substring(1,r[1].length);
  332. return r.join(".");
  333. }
  334.  
  335. function decScaleTo(x,s) { // changes x to scale s
  336. // where x is a decimal as a scaled chiliadic integer
  337. // for ease of reuse, all rounding is toward negative infinity
  338. if (s==x[0]) return x;
  339. r=new Array();
  340. if (s>x[0]) {
  341. r[0] = s;
  342. r[1] = chilMul(x[1],chilPowerOfTen(s-x[0]));
  343. return r;
  344. }
  345. // if we're here, then s<x[0]
  346. r[0]=s;
  347. r[1]=chilDiv(x[1],chilPowerOfTen(x[0]-s));
  348. return r;
  349. }
  350.  
  351. function decAdd() { // adds decimals as scaled chiliadic integers
  352. var i;
  353. var smax = 0;
  354. for (i=0; i<arguments.length; i++) {
  355. if (smax<arguments[i][0]) smax = arguments[i][0];
  356. }
  357. var mnta = new Array();
  358. for (i=0; i<arguments.length; i++) {
  359. mnta[i] = decScaleTo(arguments[i],smax)[1];
  360. }
  361. var mnts = chilAdd.apply(null, mnta);
  362. var r = new Array();
  363. r[0] = smax;
  364. r[1] = mnts;
  365. return r;
  366. }
  367.  
  368. function decNeg(x) { // negates a scaled chiliadic integer
  369. var r=new Array();
  370. r[0] = x[0];
  371. r[1] = chilNeg(x[1]);
  372. return r;
  373. }
  374.  
  375. function decSub(x,y) { // subtracts two scaled chiliadic integers
  376. return decAdd(x,decNeg(y));
  377. }
  378.  
  379. function decIsZero(x) { // tests for zero
  380. return chilIsZero(x[1]);
  381. }
  382.  
  383. function decIsPos(x) { // tests for positive
  384. return chilIsPos(x[1]);
  385. }
  386.  
  387. function decIsNeg(x) { // tests for negative
  388. return chilIsNeg(x[1]);
  389. }
  390.  
  391. function decComp(x,y) { // compares two scaled chiliadic integers
  392. // method: compare mantissa of (x-y) with 0
  393. return chilComp(decSub(x,y)[1],new Array());
  394. }
  395.  
  396. function decMul(x,y) { // multiplies two scaled chiliadic integers
  397. var r = new Array();
  398. r[0] = x[0] + y[0];
  399. r[1] = chilMul(x[1],y[1]);
  400. return r;
  401. }
  402.  
  403. function decDiv(x,y,s) { // divides x by y with scale s
  404. // x and y are scaled chiliadic integers
  405. var r = new Array();
  406. r[0] = s;
  407. r[1] = chilDiv(decScaleTo(x,y[0]+s)[1],y[1]);
  408. if (r[1]===false) return false;
  409. return r;
  410. }
  411.  
  412.  
  413.  
  414. // library ends here; now we have the functions specific to this form
  415.  
  416. function doTheMath() {
  417. var n1s = document.calcForm.n1.value;
  418. var n2s = document.calcForm.n2.value;
  419. var n1d = strToDec(n1s);
  420. var n2d = strToDec(n2s);
  421. var opnum = 0;
  422. var outtext = "Hmmm... if I've done the math right...\n";
  423. while (document.calcForm.mathradio[opnum].checked == false) opnum++;
  424. if (n1d===false || n2d===false) {
  425. alert("I need two valid numbers.\nDigits and decimal points only.\nNegative numbers are OK too.");
  426. }
  427. else if ((opnum==3 || opnum==4) && decIsZero(n2d)) {
  428. alert("Can't divide by zero.")
  429. }
  430. else if (opnum==0) {
  431. outtext += n1s +" plus "+ n2s +" equals "+decToStr(decAdd(n1d,n2d));
  432. alert(outtext);
  433. }
  434. else if (opnum==1) {
  435. outtext += n1s +" minus "+ n2s +" equals "+decToStr(decSub(n1d,n2d));
  436. alert(outtext);
  437. }
  438. else if (opnum==2) {
  439. outtext += n1s +" times "+ n2s +" equals "+decToStr(decMul(n1d,n2d));
  440. alert(outtext);
  441. }
  442. else if (opnum==3) {
  443. outtext += n1s +" divided by "+ n2s +" equals "+decToStr(decDiv(n1d,n2d,2));
  444. outtext += "\n(to 2 decimal places)"
  445. alert(outtext);
  446. }
  447. else if (opnum==4) {
  448. outtext += n1s +" divided by "+ n2s +" equals "+decToStr(decDiv(n1d,n2d,20));
  449. outtext += "\n(to 20 decimal places)"
  450. alert(outtext);
  451. }
  452. }
  453. // -->
  454. </script>
  455.  
  456. <BODY>
  457. <FORM NAME="calcForm" ACTION="" METHOD="GET">
  458. Give me a number: <BR>
  459. <INPUT TYPE="text" NAME="n1" SIZE="50" VALUE=""><BR>
  460. Give me another number: <BR>
  461. <INPUT TYPE="text" NAME="n2" SIZE="50" VALUE=""><BR>
  462. What to do with these numbers?<BR>
  463. <INPUT TYPE="radio" NAME="mathradio" VALUE="add" checked> Add them<BR>
  464. <INPUT TYPE="radio" NAME="mathradio" VALUE="sub"> Subtract them<BR>
  465. <INPUT TYPE="radio" NAME="mathradio" VALUE="mul"> Multiply them<BR>
  466. <INPUT TYPE="radio" NAME="mathradio" VALUE="div2"> Divide them (2 decimal places)<BR>
  467. <INPUT TYPE="radio" NAME="mathradio" VALUE="div20"> Divide them (20 decimal places)<BR>
  468. <INPUT TYPE="button" NAME="mathbutton" Value="Do the math" onClick="doTheMath()">
  469. </FORM>
  470. <br><br>
  471. This page and arithmetic library by Robert Lozyniak on 10 Sep 2009<br>
  472. e-mail: letter arr zero seven deuce seven one trey halfdozen eight at hotmail dot you know the rest
  473.  
  474. </body></html>
Similar Threads
Reputation Points: 10
Solved Threads: 0
Newbie Poster
rob-lozyniak is offline Offline
5 posts
since Sep 2009

This thread is more than three months old

No one has posted to this discussion for at least three months. Please let old threads die and do not reply to them unless you feel you have something new and valuable to contribute that absolutely must be added to make the discussion complete. Otherwise, please start a new thread in this forum instead.
Message:
Previous Thread in JavaScript / DHTML / AJAX Forum Timeline: Ajax executing more than once when its not ment to!!!!!
Next Thread in JavaScript / DHTML / AJAX Forum Timeline: Cant able to show jquery confirm box using scriptmanager





About Us | Contact Us | Advertise | Acceptable Use Policy
Forum Index | Build Custom RSS Feed


Follow us on Twitter


© 2011 DaniWeb® LLC