1 //Written in the D programming language
2 /*
3  * Fixed point type.
4  *
5  * Copyright 2013-2015 Jaypha
6  *
7  * Distributed under the Boost Software License, Version 1.0.
8  * (See http://www.boost.org/LICENSE_1_0.txt)
9  *
10  * Authors: Jason den Dulk
11  * Contributers: MoodleLadyCow
12  */
13 
14 /*
15  * A fixed point number is a number with a fixed number of decimal places. The
16  * number of decimal places never varies, unlike floating point types, where the
17  * number of decimal places varies depending on the value.
18  *
19  * Fixed point values are used wherever fractions are needed, but floating point
20  * values are undesirable or impractical, eg currencies.
21  *
22  * Fixed point values are precise (no rounding issues) and are integral in
23  * behaviour (division and modulo work the same as they do for integers).
24  */
25 
26 module jaypha.fixed;
27 
28 import std.string;
29 import std.math;
30 import std.conv;
31 import std.traits;
32 
33 //-----------------------------------------------------------------------------
34 struct Fixed(uint scale)
35 //-----------------------------------------------------------------------------
36 {
37   enum factor = 10^^scale;
38 
39   private:
40     enum sc = scale;
41     long value;
42     static pure nothrow Fixed make(long v) { Fixed fixed; fixed.value = v; return fixed; }
43 
44   public:
45 
46     // min and max represent the smallest and larget possible values respectively.
47 
48     static immutable Fixed min = make(long.min);
49     static immutable Fixed max = make(long.max);
50 
51     //-----------------------------------------------------
52 
53     pure nothrow this(long v) { value = v * factor; }
54     nothrow this(double v) { value = lround(v * factor); }
55     this(string v) { value = lround(std.conv.to!double(v) * factor); }
56 
57     //-----------------------------------------------------
58     // Unary operators.
59 
60     pure nothrow Fixed opUnary(string s:"++")() { value += factor; return this; }
61     pure nothrow Fixed opUnary(string s:"--")() { value -= factor; return this; }
62 
63     pure nothrow Fixed opUnary(string s)()  { mixin("return make("~s~"value);"); }
64 
65     //-----------------------------------------------------
66     // Equality
67 
68     pure nothrow bool opEquals(Fixed b) const  { return (value == b.value); }
69     pure nothrow bool opEquals(long b)  const  { return (value == b*factor); }
70     pure nothrow bool opEquals(double b) const { return ((cast(double)value / factor) == b); }
71 
72     //-----------------------------------------------------
73     // Comparison
74 
75     pure nothrow int opCmp(const Fixed b) const
76     {
77       if (value < b.value) return -1;
78       if (value > b.value) return 1;
79       return 0;
80     }
81 
82     pure nothrow int opCmp(const long b) const
83     {
84       if (value < b*factor) return -1;
85       if (value > b*factor) return 1;
86       return 0;
87     }
88 
89     pure nothrow int opCmp(const double b) const
90     {
91       if (value < b*factor) return -1;
92       if (value > b*factor) return 1;
93       return 0;
94     }
95 
96     //-----------------------------------------------------
97     // Assignment
98 
99     pure nothrow void opAssign(Fixed v) { value = v.value; }
100     pure nothrow void opAssign(long v) { value = v * factor; }
101     void opAssign(double v) { value = to!long(v * factor); }
102     void opAssign(string v)
103     { value = lround(std.conv.to!double(v) * factor); } // TODO do this without FP
104 
105     //-----------------------------------------------------
106     // Operator assignment
107 
108     pure nothrow void opOpAssign(string op)(Fixed v)
109     {
110       mixin("value = (this "~op~" v).value;");
111     }
112 
113     pure nothrow void opOpAssign(string op)(long v)
114     {
115       mixin("value = (this "~op~" v).value;");
116     }
117     nothrow void opOpAssign(string op)(double v)
118     {
119       mixin("value = (this "~op~" v).value;");
120     }
121 
122     //-----------------------------------------------------
123     // Cast and conversion
124 
125     pure nothrow T opCast(T : long)() const
126     { return value / factor; }
127 
128     pure nothrow T opCast(T : double)() const
129     { return (cast(double)value) / factor; }
130 
131     pure nothrow T opCast(T : bool)() const
132     { return value != 0; }
133 
134     // If newScale is less, then the value is rounded.
135     pure nothrow auto conv(uint newScale)() const
136     {
137       static if (newScale == scale)
138         return Fixed!scale.make(value);
139       else static if (newScale > scale)
140         return Fixed!newScale.make(value * 10^^(newScale-scale));
141       else
142       {
143         auto div = 10^^(scale-newScale);
144         auto newValue = value / div;
145         if (value%div >= div/2)
146           ++newValue;
147         return Fixed!newScale.make(newValue);
148       }
149     }
150 
151     pure @property string asString() const
152     {
153       if (value == long.min || abs(value) >= factor)
154         return format("%s.%0*d",std.conv.to!string(value/factor),scale,abs(value%factor));
155       else 
156       {
157         string sign = value >= 0 ? "" : "-";
158         return format("%s0.%0*d",sign,scale,abs(value));
159       }
160     }
161 
162     //-----------------------------------------------------
163     // Operators for Fixed and Fixed
164 
165     pure nothrow Fixed opBinary(string s:"+")(Fixed b) const
166     {
167       return make(value+b.value);
168     }
169 
170     pure nothrow Fixed opBinary(string s:"-")(Fixed b) const
171     {
172       return make(value-b.value);
173     }
174 
175     pure nothrow Fixed opBinary(string s:"*")(Fixed b) const
176     {
177       return make(value*b.value/factor);
178     }
179 
180     pure nothrow Fixed opBinary(string s:"/")(Fixed b) const
181     {
182       return make((value*factor)/b.value);
183     }
184 
185     //-----------------------------------------------------
186     // Operators for Fixed and long
187 
188     pure nothrow Fixed opBinary(string s:"+")(long b) const
189     {
190       return make(value + b*factor);
191     }
192     pure nothrow Fixed opBinaryRight(string s:"+")(long b) const
193     {
194       return make(value + b*factor);
195     }
196     pure nothrow Fixed opBinary(string s:"-")(long b) const
197     {
198       return make(value - b*factor);
199     }
200     pure nothrow Fixed opBinaryRight(string s:"-")(long b) const
201     {
202       return make(b*factor - value);
203     }
204     pure nothrow Fixed opBinary(string s:"*")(long b) const
205     {
206       return make(value*b);
207     }
208     pure nothrow Fixed opBinaryRight(string s:"*")(long b) const
209     {
210       return make(b*value);
211     }
212     pure nothrow Fixed opBinary(string s:"/")(long b) const
213     {
214       return make(value/b);
215     }
216     pure nothrow Fixed opBinaryRight(string s:"/")(long b) const
217     {
218       return make((b*factor*factor)/value);
219     }
220     pure nothrow Fixed opBinary(string s:"%")(long b) const
221     {
222       return make(value%(b*factor));
223     }
224     pure nothrow Fixed opBinaryRight(string s:"%")(long b) const
225     {
226       return make((b*factor)%value);
227     }
228 
229 
230     //-----------------------------------------------------
231     // Operators for Fixed and double
232 
233     nothrow Fixed opBinary(string s:"+")(double b) const
234     {
235       return make(value + lround(b*factor));
236     }
237     nothrow Fixed opBinaryRight(string s:"+")(double b) const
238     {
239       return make(value + lround(b*factor));
240     }
241     nothrow Fixed opBinary(string s:"-")(double b) const
242     {
243       return make(value - lround(b*factor));
244     }
245     nothrow Fixed opBinaryRight(string s:"-")(double b) const
246     {
247       return make(lround(b*factor) - value);
248     }
249     nothrow Fixed opBinary(string s:"*")(double b) const
250     {
251       return make(lround(value*b));
252     }
253     nothrow Fixed opBinaryRight(string s:"*")(double b) const
254     {
255       return make(lround(b*value));
256     }
257     nothrow Fixed opBinary(string s:"/")(double b) const
258     {
259       return make(lround(value/b));
260     }
261     nothrow Fixed opBinaryRight(string s:"/")(double b) const
262     {
263       return make(lround((b*factor*factor)/value));
264     }
265 }
266 
267 //-----------------------------------------------------------------------------
268 // T1 and T2 must be instances of Fixed.
269 
270 pure nothrow auto mult(T1, T2)(T1 op1, T2 op2)
271    if (__traits(isSame,TemplateOf!(T1), Fixed) &&
272        __traits(isSame,TemplateOf!(T2), Fixed))
273 {
274   return Fixed!(T1.sc+T2.sc).make(op1.value*op2.value);
275 }
276 
277 //-----------------------------------------------------------------------------
278 
279 alias Fixed!1 fix1;
280 alias Fixed!2 fix2;
281 alias Fixed!3 fix3;
282 
283 //-----------------------------------------------------------------------------
284 
285 unittest
286 {
287   //import std.stdio;
288 
289   // Fundamentals
290 
291   assert(fix1.factor == 10);
292   assert(fix2.factor == 100);
293   assert(fix3.factor == 1000);
294   assert(fix1.min.value == long.min);
295   assert(fix1.max.value == long.max);
296   assert(fix2.min.value == long.min);
297   assert(fix2.max.value == long.max);
298   assert(fix3.min.value == long.min);
299   assert(fix3.max.value == long.max);
300 
301   // Default
302 
303   fix2 amount;
304   assert(amount.value == 0);
305   assert(amount.asString == "0.00");
306 
307   // Creation
308 
309   fix1 v1 = 14;
310   assert(v1.value == 140);
311   fix2 v2 = -23.45;
312   assert(v2.value == -2345);
313   fix3 v3 = "134";
314   assert(v3.value == 134000);
315   fix3 v4 = "134.5";
316   assert(v4.value == 134500);
317 
318   auto v5 = fix1("22");
319   assert(v5.value == 220);
320 
321   assert(fix1(62).value == 620);
322   assert(fix2(-30).value == -3000);
323   assert(fix3("120").value == 120000);
324   assert(fix2(24.6).value == 2460);
325   assert(fix2(-27.2).value == -2720);
326   assert(fix2(16.1f).value == 1610);
327   assert(fix2(-87.3f).value == -8730);
328 
329   int i1 = 23;
330   v2 = i1;
331   assert(v2.value == 2300);
332 
333   i1 = -15;
334   v1 = i1;
335   assert(v1.value == -150);
336 
337   long l1 = 435;
338   v2 = l1;
339   assert(v2.value == 43500);
340 
341   l1 = -222;
342   v3 = l1;
343   assert(v3.value == -222000);
344 
345   // Assignment
346 
347   amount = 20;
348   assert(amount.value == 2000);
349   amount = -30L;
350   assert(amount.value == -3000);
351   amount = 13.6f;
352   assert(amount.value == 1360);
353   amount = 7.3;
354   assert(amount.value == 730);
355   amount = "-30.7";
356   assert(amount.value == -3070);
357 
358   // Comparison operators
359 
360   amount = 30;
361 
362   assert(amount == 30);
363   assert(amount != 22);
364   assert(amount <= 30);
365   assert(amount >= 30);
366   assert(amount > 29);
367   assert(!(amount > 31));
368   assert(amount < 31);
369   assert(!(amount < 29));
370 
371   amount = 22.34;
372 
373   assert(amount == 22.34);
374   assert(amount != 15.6);
375   assert(amount <= 22.34);
376   assert(amount >= 22.34);
377   assert(amount > 22.33);
378   assert(!(amount > 22.35));
379   assert(amount < 22.35);
380   assert(!(amount < 22.33));
381 
382   fix2 another = 22.34;
383   assert(amount == another);
384   assert(amount <= another);
385   assert(amount >= another);
386 
387   another = 22.35;
388   assert(amount != another);
389   assert(amount < another);
390   assert(amount <= another);
391   assert(!(amount > another));
392   assert(!(amount >= another));
393   assert(another > amount);
394   assert(another >= amount);
395   assert(!(another < amount));
396   assert(!(another <= amount));
397 
398   // Cast and conversion
399 
400   amount = 22;
401   long lVal = cast(long)amount;
402   assert(lVal == 22);
403   double dVal = cast(double)amount;
404   assert(dVal == 22.0);
405   assert(amount.asString == "22.00");
406   assert(fix2(0.15).asString == "0.15");
407   assert(fix2(-0.02).asString == "-0.02");
408   assert(fix2(-43.6).asString == "-43.60");
409   assert(fix2.min.asString == "-92233720368547758.08");
410   assert(fix2.max.asString == "92233720368547758.07");
411   bool bVal = cast(bool)amount;
412   assert(bVal == true);
413   assert(amount);
414   assert(!fix2(0));
415 
416   auto cv1 = amount.conv!1();
417   assert(cv1.factor == 10);
418   assert(cv1.value == 220);
419   auto cv3 = amount.conv!3();
420   assert(cv3.factor == 1000);
421   assert(cv3.value == 22000);
422 
423   fix3 amt3 = 3.752;
424   auto amt2 = amt3.conv!2();
425   assert(amt2.factor == 100);
426   assert(amt2.value == 375);
427   auto amt1 = amt3.conv!1();
428   assert(amt1.factor == 10);
429   assert(amt1.value == 38);
430   auto amt0 = amt3.conv!0();
431   assert(amt0.factor == 1);
432   assert(amt0.value == 4);
433 
434   // Arithmmetic operators
435 
436   fix2 op1, op2;
437 
438   op1 = 5.23;
439   op2 = 7.1;
440 
441   assert((op1 + op2) == 12.33);
442   assert((op1 - op2) == -1.87);
443   assert((op1 * op2) == 37.13);
444   assert((op1 / op2) == 0.73);
445 
446   assert(op1 + 10 == 15.23);
447   assert(op1 - 10 == -4.77);
448   assert(op1 * 10 == 52.3);
449   assert(op1 / 10 == 0.52);
450   assert(op1 % 10 == 5.23);
451 
452   assert(10 + op1 == 15.23);
453   assert(10 - op1 == 4.77);
454   assert(10 * op1 == 52.3);
455   assert(10 / op1 == 1.91);
456   assert(10 % op1 == 4.77);
457 
458   assert(op1 + 9.8 == 15.03);
459   assert(op1 - 9.8 == -4.57);
460   assert(op1 * 9.8 == 51.25);
461   assert(op1 / 9.8 == 0.53);
462 
463   assert(9.8 + op1 == 15.03);
464   assert(9.8 - op1 == 4.57);
465   assert(9.8 * op1 == 51.25);
466 
467   assert(9.8 / op1 == 1.87);
468 
469   assert(op1 != op2);
470   assert(op1 == fix2(5.23));
471   assert(op2 == fix2("7.1"));
472   assert(op2 != fix2("7.09"));
473   assert(op2 != fix2("7.11"));
474 
475   // Increment, decrement
476 
477   amount = 20;
478   assert(++amount == 21);
479   assert(amount == 21);
480   assert(--amount == 20);
481   assert(amount == 20);
482   assert(-amount == -20);
483   assert(amount == 20);
484 
485   amount = amount + 14;
486   assert(amount.value == 3400);
487 
488   amount = 6 + amount;
489   assert(amount.value == 4000);
490 
491   // Assignment operators.
492 
493   amount = 40;
494 
495   amount += 5;
496   assert(amount.value == 4500);
497 
498   amount -= 6.5;
499   assert(amount.value == 3850);
500 
501   another = -4;
502 
503   amount += another;
504   assert(amount.value == 3450);
505 
506   amount *= 1.5;
507   assert(amount.value == 5175);
508 
509   amount /= 12;
510   assert(amount.value == 431);
511 
512   // More tests.
513 
514   amount = 0.05;
515   assert(amount.value == 5);
516   assert(amount == 0.05);
517   assert(amount.asString == "0.05");
518   assert(cast(long)amount == 0);
519   assert(cast(double)amount == 0.05);
520 
521   amount = 1.05;
522   assert(amount.value == 105);
523   assert(amount == 1.05);
524   assert(amount.asString == "1.05");
525   assert(cast(long)amount == 1);
526   assert(cast(double)amount == 1.05);
527 
528   assert((++amount).value == 205);
529   assert(amount.value == 205);
530   assert((-amount).value == -205);
531   assert(--amount == 1.05);
532   assert(amount.value == 105);
533 
534   amount = 50;
535   assert(amount.value == 5000);
536 
537 
538   another = amount * 2;
539   assert(another.value == 10000);
540   amount *= 3;
541   assert(amount.value == 15000);
542 
543   amount = "30";
544   assert(amount.value == 3000);
545 
546   amount = 295;
547   amount /= 11;
548   assert(amount.value == 2681);
549   assert(amount == 26.81);
550 
551   amount = 295;
552   another = 11;
553   assert((amount/another).value == 2681);
554   assert((amount/another).asString == "26.81");
555 
556   another = amount + 1.3;
557   assert(another.value == 29630);
558 
559   amount = 30;
560   another = 50.2 - amount;
561   assert(another.value == 2020);
562   another -= 50;
563 
564   assert(another.value == -2980);
565   assert(another == -29.8);
566 
567   another = amount/1.6;
568   assert(another.value == 1875);
569 
570   another = amount*1.56;
571   assert(another.value == 4680);
572 
573   fix1 a = 3.2;
574   fix2 b = 1.15;
575 
576   assert(a.value == 32);
577   assert(b.value == 115);
578 
579   assert(fix2(334) / 15 == 22.26);
580   assert(fix2(334) % 10 == 4);
581 
582   assert(334 / fix2(15.3) == 21.83);
583   assert(334 % fix2(15.3) == 12.7);
584 
585   // mult function
586 
587   fix2 _v1 = 1.27;
588   fix2 _v2 = 3.45;
589   auto _v = _v1.mult(_v2);
590   assert(_v.sc == 4);
591   assert(_v.value == 43815);
592 }