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, Pablo De Nápoli
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 import std.stdio;
33 import std.regex;
34 import std.format;
35 
36 //-----------------------------------------------------------------------------
37 struct Fixed(uint scale)
38 //-----------------------------------------------------------------------------
39 {
40   enum factor = 10^^scale;
41   //enum pattern = ctRegex!(format!"^[0-9]+(.[0-9]{1,%s})?$"(scale)); <-- This would be preferred, except it breaks when scale = 0.
42   enum pattern = ctRegex!("^[+,-]?[0-9]+(\\.[0-9]+)?$");
43 
44   private:
45     enum sc = scale;
46     long value;
47     static pure nothrow Fixed make(long v) { Fixed fixed; fixed.value = v; return fixed; }
48 
49     // Converts from a string to a long usable by this class.
50     static long fromString_(string v)
51     {
52       if (matchFirst(v, pattern).empty)
53         throw new Exception(format("Fixed!%s: Input string '%s' is invalid",scale,v));
54 
55       auto words = split(v, ".");
56       auto tmp = to!long(words[0]) * factor;
57       if (words.length > 1) {
58         if (words[1].length > scale)
59           throw new Exception(format("Fixed!%s: Input string '%s' is invalid",scale,v));
60         auto fractionPart = to!long(words[1]) * 10^^(scale-words[1].length);
61         if (tmp < 0)
62           tmp -= fractionPart;
63         else
64           tmp += fractionPart;
65       }
66       return tmp;
67     }
68 
69   public:
70 
71     // min and max represent the smallest and larget possible values respectively.
72 
73     static immutable Fixed min = make(long.min);
74     static immutable Fixed max = make(long.max);
75 
76     //-----------------------------------------------------
77 
78     pure nothrow this(long v) { value = v * factor; }
79     nothrow this(double v) { value = lround(v * factor); }
80     this(string v) { value = fromString_(v); }
81 
82 
83 
84     //-----------------------------------------------------------------------------
85     // integralPart & decimalPart
86     
87     nothrow long integralPart() const
88     {
89       return value/factor;
90     }
91     nothrow long integralPart(long newIntegralPart)
92     {
93       if (newIntegralPart<0)
94         value = newIntegralPart * factor + decimalPart*-1;
95       else
96         value = newIntegralPart * factor + decimalPart;
97       return newIntegralPart;
98     }
99 
100     /++
101     + this returns |x| - ⌊|x|⌋, so it's always positive
102     +/
103     nothrow long decimalPart() const
104     {
105       return abs(value%factor);
106     }
107     /++
108     + sign doesn't matter, for instance those two calls are equivalent:
109     +  - fix2(-3).decimalPart = -14 // produces -3.14
110     +  - fix2(-3).decimalPart =  14 // same
111     + 
112     + if the argument is too big, it is truncated
113     +/
114     nothrow long decimalPart(long newDecimalPart)
115     {
116       if ((value < 0) != (newDecimalPart < 0))
117         newDecimalPart *= -1;
118       while (newDecimalPart > factor)
119         newDecimalPart /= 10;
120       value = integralPart * factor + newDecimalPart;
121       return newDecimalPart;
122     }
123 
124     //-----------------------------------------------------
125     //  This function was added so that vibe recognize Fixed as
126     // string serializable.
127     // See: http://vibed.org/api/vibe.data.serialization/isStringSerializable
128     //-----------------------------------------------------
129     
130     static Fixed!scale fromString(string v){ return Fixed!scale(v);}
131 
132 
133     //-----------------------------------------------------
134     // Unary operators.
135 
136     pure nothrow Fixed opUnary(string s:"++")() { value += factor; return this; }
137     pure nothrow Fixed opUnary(string s:"--")() { value -= factor; return this; }
138 
139     pure nothrow Fixed opUnary(string s)()  { mixin("return make("~s~"value);"); }
140 
141     //-----------------------------------------------------
142     // Equality
143 
144     pure nothrow bool opEquals(Fixed b) const  { return (value == b.value); }
145     pure nothrow bool opEquals(long b)  const  { return (value == b*factor); }
146     pure nothrow bool opEquals(double b) const { return ((cast(double)value / factor) == b); }
147 
148     //-----------------------------------------------------
149     // Comparison
150 
151     pure nothrow int opCmp(const Fixed b) const
152     {
153       if (value < b.value) return -1;
154       if (value > b.value) return 1;
155       return 0;
156     }
157 
158     pure nothrow int opCmp(const long b) const
159     {
160       if (value < b*factor) return -1;
161       if (value > b*factor) return 1;
162       return 0;
163     }
164 
165     pure nothrow int opCmp(const double b) const
166     {
167       if (value < b*factor) return -1;
168       if (value > b*factor) return 1;
169       return 0;
170     }
171 
172     //-----------------------------------------------------
173     // Assignment
174 
175     pure nothrow void opAssign(Fixed v) { value = v.value; }
176     pure nothrow void opAssign(long v) { value = v * factor; }
177     void opAssign(double v) { value = to!long(v * factor); }
178     void opAssign(string v) { value = fromString_(v); }
179 
180     //-----------------------------------------------------
181     // Operator assignment
182 
183     pure nothrow void opOpAssign(string op)(Fixed v)
184     {
185       mixin("value = (this "~op~" v).value;");
186     }
187 
188     pure nothrow void opOpAssign(string op)(long v)
189     {
190       mixin("value = (this "~op~" v).value;");
191     }
192     nothrow void opOpAssign(string op)(double v)
193     {
194       mixin("value = (this "~op~" v).value;");
195     }
196 
197     //-----------------------------------------------------
198     // Cast and conversion
199 
200     pure nothrow T opCast(T : long)() const
201     { return integralPart; }
202 
203     pure nothrow T opCast(T : double)() const
204     { return (cast(double)value) / factor; }
205 
206     pure nothrow T opCast(T : bool)() const
207     { return value != 0; }
208 
209     // If newScale is less, then the value is rounded.
210     pure nothrow auto conv(uint newScale)() const
211     {
212       static if (newScale == scale)
213         return Fixed!scale.make(value);
214       else static if (newScale > scale)
215         return Fixed!newScale.make(value * 10^^(newScale-scale));
216       else
217       {
218         auto div = 10^^(scale-newScale);
219         auto newValue = value / div;
220         if (value%div >= div/2)
221           ++newValue;
222         return Fixed!newScale.make(newValue);
223       }
224     }
225 
226     pure @property string asString() const
227     {
228       return toString();
229     }
230 
231     pure string toString() const
232     {
233       if (value == long.min || abs(value) >= factor)
234         return format("%s.%0*d",std.conv.to!string(integralPart),scale,decimalPart);
235       else 
236       {
237         string sign = value >= 0 ? "" : "-";
238         return format("%s0.%0*d",sign,scale,abs(value));
239       }
240     }
241 
242 
243     //-----------------------------------------------------
244     // Operators for Fixed and Fixed
245 
246     pure nothrow Fixed opBinary(string s:"+")(Fixed b) const
247     {
248       return make(value+b.value);
249     }
250 
251     pure nothrow Fixed opBinary(string s:"-")(Fixed b) const
252     {
253       return make(value-b.value);
254     }
255 
256     pure nothrow Fixed opBinary(string s:"*")(Fixed b) const
257     {
258       return make(value*b.value/factor);
259     }
260 
261     pure nothrow Fixed opBinary(string s:"/")(Fixed b) const
262     {
263       return make((value*factor)/b.value);
264     }
265 
266     //-----------------------------------------------------
267     // Operators for Fixed and long
268 
269     pure nothrow Fixed opBinary(string s:"+")(long b) const
270     {
271       return make(value + b*factor);
272     }
273     pure nothrow Fixed opBinaryRight(string s:"+")(long b) const
274     {
275       return make(value + b*factor);
276     }
277     pure nothrow Fixed opBinary(string s:"-")(long b) const
278     {
279       return make(value - b*factor);
280     }
281     pure nothrow Fixed opBinaryRight(string s:"-")(long b) const
282     {
283       return make(b*factor - value);
284     }
285     pure nothrow Fixed opBinary(string s:"*")(long b) const
286     {
287       return make(value*b);
288     }
289     pure nothrow Fixed opBinaryRight(string s:"*")(long b) const
290     {
291       return make(b*value);
292     }
293     pure nothrow Fixed opBinary(string s:"/")(long b) const
294     {
295       return make(value/b);
296     }
297     pure nothrow Fixed opBinaryRight(string s:"/")(long b) const
298     {
299       return make((b*factor*factor)/value);
300     }
301     pure nothrow Fixed opBinary(string s:"%")(long b) const
302     {
303       return make(value%(b*factor));
304     }
305     pure nothrow Fixed opBinaryRight(string s:"%")(long b) const
306     {
307       return make((b*factor)%value);
308     }
309 
310 
311     //-----------------------------------------------------
312     // Operators for Fixed and double
313 
314     nothrow Fixed opBinary(string s:"+")(double b) const
315     {
316       return make(value + lround(b*factor));
317     }
318     nothrow Fixed opBinaryRight(string s:"+")(double b) const
319     {
320       return make(value + lround(b*factor));
321     }
322     nothrow Fixed opBinary(string s:"-")(double b) const
323     {
324       return make(value - lround(b*factor));
325     }
326     nothrow Fixed opBinaryRight(string s:"-")(double b) const
327     {
328       return make(lround(b*factor) - value);
329     }
330     nothrow Fixed opBinary(string s:"*")(double b) const
331     {
332       return make(lround(value*b));
333     }
334     nothrow Fixed opBinaryRight(string s:"*")(double b) const
335     {
336       return make(lround(b*value));
337     }
338     nothrow Fixed opBinary(string s:"/")(double b) const
339     {
340       return make(lround(value/b));
341     }
342     nothrow Fixed opBinaryRight(string s:"/")(double b) const
343     {
344       return make(lround((b*factor*factor)/value));
345     }
346 }
347 
348 //-----------------------------------------------------------------------------
349 // T1 and T2 must be instances of Fixed.
350 
351 pure nothrow auto mult(T1, T2)(T1 op1, T2 op2)
352    if (__traits(isSame,TemplateOf!(T1), Fixed) &&
353        __traits(isSame,TemplateOf!(T2), Fixed))
354 {
355   return Fixed!(T1.sc+T2.sc).make(op1.value*op2.value);
356 }
357 
358 //-----------------------------------------------------------------------------
359 
360 alias Fixed!1 fix1;
361 alias Fixed!2 fix2;
362 alias Fixed!3 fix3;
363 
364 //-----------------------------------------------------------------------------
365 
366 unittest
367 {
368   //import std.stdio;
369 
370   // Fundamentals
371 
372   assert(fix1.factor == 10);
373   assert(fix2.factor == 100);
374   assert(fix3.factor == 1000);
375   assert(fix1.min.value == long.min);
376   assert(fix1.max.value == long.max);
377   assert(fix2.min.value == long.min);
378   assert(fix2.max.value == long.max);
379   assert(fix3.min.value == long.min);
380   assert(fix3.max.value == long.max);
381 
382   // Default
383 
384   fix2 amount;
385   assert(amount.value == 0);
386   assert(amount.toString() == "0.00");
387   assert(amount.asString == "0.00");
388 
389   // Creation
390 
391   fix1 v1 = 14;
392   assert(v1.value == 140);
393   fix2 v2 = -23.45;
394   assert(v2.value == -2345);
395   fix3 v3 = "134";
396   assert(v3.value == 134000);
397   fix3 v4 = "134.5";
398   assert(v4.value == 134500);
399   fix3 v5 = "134.55";
400   assert(v5.value == 134550);
401   fix3 v6 = "134.557";
402   assert(v6.value == 134557);
403 
404   auto v7 = fix1("22");
405   assert(v7.value == 220);
406 
407   assert(fix1(62).value == 620);
408   assert(fix2(-30).value == -3000);
409   assert(fix3("120").value == 120000);
410   assert(fix2(24.6).value == 2460);
411   assert(fix2(-27.2).value == -2720);
412   assert(fix2(16.1f).value == 1610);
413   assert(fix2(-87.3f).value == -8730);
414 
415   int i1 = 23;
416   v2 = i1;
417   assert(v2.value == 2300);
418 
419   i1 = -15;
420   v1 = i1;
421   assert(v1.value == -150);
422 
423   long l1 = 435;
424   v2 = l1;
425   assert(v2.value == 43500);
426 
427   l1 = -222;
428   v3 = l1;
429   assert(v3.value == -222000);
430 
431   // Assignment
432 
433   amount = 20;
434   assert(amount.value == 2000);
435   amount = -30L;
436   assert(amount.value == -3000);
437   amount = 13.6f;
438   assert(amount.value == 1360);
439   amount = 7.3;
440   assert(amount.value == 730);
441   amount = "-30.7";
442   assert(amount.value == -3070);
443   amount = "200.06";
444   assert(amount.value == 20006);
445 
446   try {
447     // Cannot assign a non-number
448     amount = "xyz";
449     // Cannot assign a number with more decimal places than the scale.
450     amount = "-30.787";
451     assert(false);
452   } catch (Exception e) {
453   }
454 
455   // Comparison operators
456 
457   amount = 30;
458 
459   assert(amount == 30);
460   assert(amount != 22);
461   assert(amount <= 30);
462   assert(amount >= 30);
463   assert(amount > 29);
464   assert(!(amount > 31));
465   assert(amount < 31);
466   assert(!(amount < 29));
467 
468   amount = 22.34;
469 
470   assert(amount == 22.34);
471   assert(amount != 15.6);
472   assert(amount <= 22.34);
473   assert(amount >= 22.34);
474   assert(amount > 22.33);
475   assert(!(amount > 22.35));
476   assert(amount < 22.35);
477   assert(!(amount < 22.33));
478 
479   fix2 another = 22.34;
480   assert(amount == another);
481   assert(amount <= another);
482   assert(amount >= another);
483 
484   another = 22.35;
485   assert(amount != another);
486   assert(amount < another);
487   assert(amount <= another);
488   assert(!(amount > another));
489   assert(!(amount >= another));
490   assert(another > amount);
491   assert(another >= amount);
492   assert(!(another < amount));
493   assert(!(another <= amount));
494 
495   // Cast and conversion
496 
497   amount = 22;
498   long lVal = cast(long)amount;
499   assert(lVal == 22);
500   double dVal = cast(double)amount;
501   assert(dVal == 22.0);
502   assert(amount.toString() == "22.00");
503   assert(fix2(0.15).toString() == "0.15");
504   assert(fix2(-0.02).toString() == "-0.02");
505   assert(fix2(-43.6).toString() == "-43.60");
506   assert(fix2.min.toString() == "-92233720368547758.08");
507   assert(fix2.max.toString() == "92233720368547758.07");
508   bool bVal = cast(bool)amount;
509   assert(bVal == true);
510   assert(amount);
511   assert(!fix2(0));
512 
513   auto cv1 = amount.conv!1();
514   assert(cv1.factor == 10);
515   assert(cv1.value == 220);
516   auto cv3 = amount.conv!3();
517   assert(cv3.factor == 1000);
518   assert(cv3.value == 22000);
519 
520   fix3 amt3 = 3.752;
521   auto amt2 = amt3.conv!2();
522   assert(amt2.factor == 100);
523   assert(amt2.value == 375);
524   auto amt1 = amt3.conv!1();
525   assert(amt1.factor == 10);
526   assert(amt1.value == 38);
527   auto amt0 = amt3.conv!0();
528   assert(amt0.factor == 1);
529   assert(amt0.value == 4);
530 
531   // Arithmmetic operators
532 
533   fix2 op1, op2;
534 
535   op1 = 5.23;
536   op2 = 7.1;
537 
538   assert((op1 + op2) == 12.33);
539   assert((op1 - op2) == -1.87);
540   assert((op1 * op2) == 37.13);
541   assert((op1 / op2) == 0.73);
542 
543   assert(op1 + 10 == 15.23);
544   assert(op1 - 10 == -4.77);
545   assert(op1 * 10 == 52.3);
546   assert(op1 / 10 == 0.52);
547   assert(op1 % 10 == 5.23);
548 
549   assert(10 + op1 == 15.23);
550   assert(10 - op1 == 4.77);
551   assert(10 * op1 == 52.3);
552   assert(10 / op1 == 1.91);
553   assert(10 % op1 == 4.77);
554 
555   assert(op1 + 9.8 == 15.03);
556   assert(op1 - 9.8 == -4.57);
557   assert(op1 * 9.8 == 51.25);
558   assert(op1 / 9.8 == 0.53);
559 
560   assert(9.8 + op1 == 15.03);
561   assert(9.8 - op1 == 4.57);
562   assert(9.8 * op1 == 51.25);
563 
564   assert(9.8 / op1 == 1.87);
565 
566   assert(op1 != op2);
567   assert(op1 == fix2(5.23));
568   assert(op2 == fix2("7.1"));
569   assert(op2 != fix2("7.09"));
570   assert(op2 != fix2("7.11"));
571 
572   // Increment, decrement
573 
574   amount = 20;
575   assert(++amount == 21);
576   assert(amount == 21);
577   assert(--amount == 20);
578   assert(amount == 20);
579   assert(-amount == -20);
580   assert(amount == 20);
581 
582   amount = amount + 14;
583   assert(amount.value == 3400);
584 
585   amount = 6 + amount;
586   assert(amount.value == 4000);
587 
588   // Assignment operators.
589 
590   amount = 40;
591 
592   amount += 5;
593   assert(amount.value == 4500);
594 
595   amount -= 6.5;
596   assert(amount.value == 3850);
597 
598   another = -4;
599 
600   amount += another;
601   assert(amount.value == 3450);
602 
603   amount *= 1.5;
604   assert(amount.value == 5175);
605 
606   amount /= 12;
607   assert(amount.value == 431);
608 
609   assert(Fixed!2.fromString("2.5") == Fixed!2("2.5"));
610   
611   // The following template is copied from vibe.d sources
612   // Copyright (c) 2012-2018 RejectedSoftware e.K.
613   // which is permited by vibe.d licence (MIT public license, 
614   // see http://vibed.org/about#license)
615   // in order to test the fromString method in the exact way that vibe.d does
616   
617   template isStringSerializable(T)
618   {
619 	enum bool isStringSerializable = is(typeof(T.init.toString()) : string) && is(typeof(T.fromString("")) : T);
620   }
621  
622  alias number= Fixed!2; 
623  static assert(isStringSerializable!number);
624 
625   // More tests.
626 
627   amount = 0.05;
628   assert(amount.value == 5);
629   assert(amount == 0.05);
630   assert(amount.toString() == "0.05");
631   assert(cast(long)amount == 0);
632   assert(cast(double)amount == 0.05);
633 
634   amount = 1.05;
635   assert(amount.value == 105);
636   assert(amount == 1.05);
637   assert(amount.toString() == "1.05");
638   assert(cast(long)amount == 1);
639   assert(cast(double)amount == 1.05);
640 
641   assert((++amount).value == 205);
642   assert(amount.value == 205);
643   assert((-amount).value == -205);
644   assert(--amount == 1.05);
645   assert(amount.value == 105);
646 
647   amount = 50;
648   assert(amount.value == 5000);
649 
650 
651   another = amount * 2;
652   assert(another.value == 10000);
653   amount *= 3;
654   assert(amount.value == 15000);
655 
656   amount = "30";
657   assert(amount.value == 3000);
658 
659   amount = 295;
660   amount /= 11;
661   assert(amount.value == 2681);
662   assert(amount == 26.81);
663 
664   amount = 295;
665   another = 11;
666   assert((amount/another).value == 2681);
667   assert((amount/another).toString() == "26.81");
668 
669   another = amount + 1.3;
670   assert(another.value == 29630);
671 
672   amount = 30;
673   another = 50.2 - amount;
674   assert(another.value == 2020);
675   another -= 50;
676 
677   assert(another.value == -2980);
678   assert(another == -29.8);
679 
680   another = amount/1.6;
681   assert(another.value == 1875);
682 
683   another = amount*1.56;
684   assert(another.value == 4680);
685 
686   fix1 a = 3.2;
687   fix2 b = 1.15;
688 
689   assert(a.value == 32);
690   assert(b.value == 115);
691 
692   assert(fix2(334) / 15 == 22.26);
693   assert(fix2(334) % 10 == 4);
694 
695   assert(334 / fix2(15.3) == 21.83);
696   assert(334 % fix2(15.3) == 12.7);
697 
698   // mult function
699 
700   fix2 _v1 = 1.27;
701   fix2 _v2 = 3.45;
702   auto _v = _v1.mult(_v2);
703   assert(_v.sc == 4);
704   assert(_v.value == 43815);
705   
706   
707   // integralPart & decimalPart
708   {
709     fix2 aa = 123.45;
710     assert(aa.integralPart == 123);
711     assert(aa.decimalPart == 45);
712     
713     aa = -98765.43;
714     assert(aa.integralPart == -98765);
715     assert(aa.decimalPart == 43);
716     
717     aa.integralPart = 0;
718     assert(aa == fix2(0.43));
719     aa.integralPart(aa.integralPart - 1);
720     aa.integralPart = aa.integralPart - 1;
721     assert(aa.integralPart == -2);
722     assert(aa.decimalPart == 43);
723     
724     aa.decimalPart = 99;
725     assert(aa == fix2(-2.99));
726     aa.decimalPart = -98;
727     assert(aa == fix2(-2.98));
728     aa.integralPart = 5;
729     assert(aa == fix2(5.98));
730   }
731 }