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 }