001/* The numeric rounding strategy classes. 002 003 Copyright (c) 2002-2014 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 */ 028package ptolemy.math; 029 030import java.math.BigDecimal; 031import java.math.BigInteger; 032import java.util.HashMap; 033import java.util.Iterator; 034import java.util.Map; 035 036/////////////////////////////////////////////////////////////////// 037//// Rounding 038 039/** 040 The Rounding class provides a type safe enumeration of strategies for 041 handling loss of numeric resolution when rounding. Rounding is typically 042 resolved when quantization constraints are applied to a computed result 043 in order to satisfy the requirements of the type to which the result is to be 044 assigned. 045 <p> 046 Rounding is an abstract class for all rounding strategies. 047 The primary method of this class is {@link #round(BigDecimal) round}. This 048 method will round a BigDecimal value to the appropriate integer 049 and return a BigInteger object.</p> 050 <p> 051 {@link BigDecimal} objects are rounded by calling the 052 {@link BigDecimal#setScale(int)} method with the appropriate 053 rounding mode. Several static methods are provided in this class 054 to support this functionality for each of the supported rounding 055 modes. In addition, several static singleton rounding strategies 056 are provided by this class to implement one of the supported 057 routing modes. Each of these rounding strategies are modeled 058 after the rounding strategies provided by {@link BigDecimal} 059 and include:</p> 060 061 <ul> 062 063 <li> <i><b>up</b></i> <br> 064 Rounding mode to round away from zero. 065 Always increments the digit prior to a non-zero discarded fraction. 066 Note that this rounding mode never decreases the magnitude of 067 the calculated value. 068 This rounding mode is supported by the static 069 {@link #roundUp(BigDecimal) roundUp} method and the Rounding 070 singletons {@link #GENERAL}, {@link #UNKNOWN} and {@link #UP}.</li> 071 072 <li> <i><b>down</b></i> <br> 073 Rounding mode to round towards zero. 074 Never increments the digit prior to a discarded fraction 075 (i.e., truncates). Note that this rounding mode never increases 076 the magnitude of the calculated value. 077 This rounding mode is supported by the static 078 {@link #roundDown(BigDecimal) roundDown} method and the Rounding 079 singleton {@link #DOWN}.</li> 080 081 <li> <i><b>floor</b></i> <br> 082 Rounding mode to round towards negative infinity. 083 If decimal is positive, behave as <b>round down</b>; 084 if decimal is negative, behave as <b>round up</b>. 085 This rounding mode is supported by the static 086 {@link #roundFloor(BigDecimal) roundFloor} method and the Rounding 087 singleton {@link #FLOOR}.</li> 088 089 <li> <i><b>ceiling</b></i> <br> 090 Rounding mode to round towards positive infinity. 091 If decimal is positive, behave as <b>round up</b>; 092 if decimal is negative, behave as <b>round down</b>. 093 This rounding mode is supported by the static 094 {@link #roundCeiling(BigDecimal) roundCeiling} method and the Rounding 095 singleton {@link #CEILING}.</li> 096 097 <li> <i><b>half up</b></i> <br> 098 Rounding mode to round towards "nearest neighbor" unless 099 both neighbors are equidistant, in which case round up. 100 Behaves as for <b>round up</b> if the discarded fraction is ≥ .5; 101 otherwise, behaves as for <b>round down</b>. Note that this is the 102 rounding mode that most of us were taught in grade school. 103 Rounding mode to round towards zero. 104 This rounding mode is supported by the static 105 {@link #roundHalfUp(BigDecimal) roundHalfUp} method and the Rounding 106 singleton {@link #HALF_UP}.</li> 107 108 <li> <i><b>half down</b></i> <br> 109 Rounding mode to round towards "nearest neighbor" unless 110 both neighbors are equidistant, in which case round down. 111 Behaves as for <b>round up</b> if the discarded fraction is > .5; 112 otherwise, behaves as for <b>ROUND_DOWN</b>. 113 This rounding mode is supported by the static 114 {@link #roundHalfDown(BigDecimal) roundHalfDown} method and the Rounding 115 singleton {@link #HALF_DOWN}.</li> 116 117 <li> <i><b>half even</b></i> <br> 118 Rounding mode to round towards the "nearest neighbor" unless 119 both neighbors are equidistant, in which case, round towards 120 the even neighbor. Behaves as for <b>round half up</b> 121 if the digit to the left of the discarded fraction is odd; 122 behaves as for <b>round half down</b> if it's even. 123 Note that this is the rounding 124 mode that minimizes cumulative error when applied repeatedly 125 over a sequence of calculations. 126 This rounding mode is supported by the static 127 {@link #roundHalfEven(BigDecimal) roundHalfEven} method and the Rounding 128 singletons {@link #HALF_EVEN} and {@link #CONVERGENT}.</li> 129 130 <li> <i><b>half floor</b></i> <br> 131 Rounding mode to round towards "nearest neighbor" unless 132 both neighbors are equidistant, in which case round 133 "ceiling". Behaves as <b>round half down</b> 134 if the decimal is positive and as <b>round half up</b> 135 if the decimal is negative. Note that there is no half floor rounding 136 mode supported for BigDecimal values. 137 This rounding mode is supported by the static 138 {@link #roundHalfFloor(BigDecimal) roundHalfFloor} method and 139 the Rounding singleton {@link #HALF_FLOOR}.</li> 140 141 <li> <i><b>half ceiling</b></i> <br> 142 Rounding mode to round towards "nearest neighbor" unless 143 both neighbors are equidistant, in which case round 144 "ceiling". Behaves as <b>round half up</b> if the decimal 145 is positive and as <b>round half down</b> 146 if the decimal is negative. 147 Note that there is no half ceiling rounding mode 148 supported for BigDecimal values. 149 This rounding mode is supported by the static 150 {@link #roundHalfFloor(BigDecimal) roundHalfCeiling} method and 151 the Rounding singleton {@link #HALF_CEILING}.</li> 152 153 </ul> 154 155 156 <p>A specific strategy may be chosen dynamically by invoking forName() or 157 getName() with one of the above strategy names. Alternatively a strategy 158 may be selected by using one of the static singletons.</p> 159 <p> 160 The <i>truncate</i> and <i>nearest</i> strategies should be 161 preferred since they 162 correspond to capabilities available on many processors. Other 163 rounding strategies may require costly code on practical hardware.</p> 164 <p> 165 The active class functionality is provided by the quantize method which is 166 normally invoked from Quantization.quantize.</p> 167 168 @author Ed Willink, Contributor: Mike Wirthlin 169 @version $Id$ 170 @since Ptolemy II 2.1 171 @Pt.ProposedRating Red (Ed.Willink) 172 @Pt.AcceptedRating Red 173 */ 174public abstract class Rounding implements Cloneable { 175 176 /////////////////////////////////////////////////////////////////// 177 //// public methods //// 178 179 /** Return this, that is, return the reference to this object. 180 * @return This Rounding. 181 */ 182 @Override 183 public Object clone() { 184 return this; 185 } 186 187 /** Determine if the argument represents the same Rounding as this 188 * object. 189 * @param object Another object. 190 * @return True if the argument represents the same Rounding as 191 * this object; false otherwise. 192 */ 193 @Override 194 public boolean equals(Object object) { 195 // since Rounding is a type safe enumeration, can use == to 196 // test equality. 197 return this == object; 198 } 199 200 /** Return an instance of this class with the specified name. 201 * @param name Rounding mode name. 202 * @return An instance of Rounding or null if the given name 203 * does not match a valid rounding mode. 204 */ 205 public static Rounding forName(String name) { 206 return (Rounding) _nameToRounding.get(name); 207 } 208 209 /** Return an instance of this class with the specified name, 210 * or null if none exists. 211 * 212 * @param name The name of the Rounding strategy to find. 213 * @return An instance of Rounding. 214 * @exception IllegalArgumentException If the string does not 215 * match one of the known strategies. 216 */ 217 public static Rounding getName(String name) 218 throws IllegalArgumentException { 219 Rounding rounding = (Rounding) _nameToRounding.get(name); 220 221 if (rounding != null) { 222 return rounding; 223 } 224 225 throw new IllegalArgumentException( 226 "Unknown rounding strategy \"" + name + "\"."); 227 } 228 229 /** Return a hash code value for this object. */ 230 @Override 231 public int hashCode() { 232 return _name.hashCode(); 233 } 234 235 /** 236 * Return an iterator for the names of all overflow types. 237 * @return An iterator for the names of all overflow types. 238 */ 239 public static Iterator nameIterator() { 240 return _nameToRounding.keySet().iterator(); 241 } 242 243 /** 244 * Round the BigDecimal value using the appropriate rounding 245 * strategy. The result is a BigInteger value rounded 246 * appropriately. Each class that extends Rounding will provide 247 * a mode specific round function. 248 * 249 * @param decimal value to be rounded 250 * @return The rounded BigInteger. 251 */ 252 public abstract BigInteger round(BigDecimal decimal); 253 254 /** Rounding mode to round towards positive infinity. 255 * If decimal is positive, behave as {@link #roundUp}; 256 * if decimal is negative, behave as {@link #roundDown}. 257 * 258 * @see BigDecimal#ROUND_CEILING 259 * 260 * @param decimal The BigDecimal value to round. 261 * @return Rounded BigDecimal value 262 * **/ 263 public static BigDecimal roundCeiling(BigDecimal decimal) { 264 return decimal.setScale(0, BigDecimal.ROUND_CEILING); 265 } 266 267 /** Rounding mode to round towards zero. 268 * Never increments the digit prior to a discarded fraction 269 * (i.e., truncates). Note that this rounding mode never increases 270 * the magnitude of the calculated value. 271 * 272 * @see BigDecimal#ROUND_DOWN 273 * 274 * @param decimal The BigDecimal value to round. 275 * @return Rounded BigDecimal value 276 * **/ 277 public static BigDecimal roundDown(BigDecimal decimal) { 278 return decimal.setScale(0, BigDecimal.ROUND_DOWN); 279 } 280 281 /** Rounding mode to round towards negative infinity. 282 * If decimal is positive, behave as {@link #roundDown}; 283 * if decimal is negative, behave as {@link #roundUp}. 284 * 285 * @see BigDecimal#ROUND_FLOOR 286 * 287 * @param decimal The BigDecimal value to round. 288 * @return Rounded BigDecimal value 289 * **/ 290 public static BigDecimal roundFloor(BigDecimal decimal) { 291 return decimal.setScale(0, BigDecimal.ROUND_FLOOR); 292 } 293 294 /** Rounding mode to round towards "nearest neighbor" unless 295 * both neighbors are equidistant, in which case round 296 * "ceiling". 297 * Behaves as HALF_ROUND_UP if the decimal is positive and 298 * as HALF_ROUND_DOWN if the decimal is negative. 299 * <p> 300 * 301 * Note that there is no half ceiling rounding mode 302 * supported for BigDecimal values. This method uses a 303 * combination of the {@link BigDecimal#ROUND_HALF_UP} and 304 * {@link BigDecimal#ROUND_HALF_DOWN} to perform this 305 * new rounding mode. 306 * 307 * @param decimal The BigDecimal value to round. 308 * @return Rounded BigDecimal value 309 * **/ 310 public static BigDecimal roundHalfCeiling(BigDecimal decimal) { 311 if (decimal.signum() == -1) { 312 return roundHalfDown(decimal); 313 } 314 return roundHalfUp(decimal); 315 } 316 317 /** Rounding mode to round towards "nearest neighbor" unless 318 * both neighbors are equidistant, in which case round down. 319 * Behaves as for ROUND_UP if the discarded fraction is > .5; 320 * otherwise, behaves as for ROUND_DOWN. 321 * 322 * @see BigDecimal#ROUND_HALF_UP 323 * 324 * @param decimal The BigDecimal value to round. 325 * @return Rounded BigDecimal value 326 * **/ 327 public static BigDecimal roundHalfDown(BigDecimal decimal) { 328 return decimal.setScale(0, BigDecimal.ROUND_HALF_DOWN); 329 } 330 331 /** Rounding mode to round towards the "nearest neighbor" unless 332 * both neighbors are equidistant, in which case, round towards 333 * the even neighbor. Behaves as for ROUND_HALF_UP if the digit 334 * to the left of the discarded fraction is odd; behaves as for 335 * ROUND_HALF_DOWN if it's even. Note that this is the rounding 336 * mode that minimizes cumulative error when applied repeatedly 337 * over a sequence of calculations. 338 * 339 * @see BigDecimal#ROUND_HALF_EVEN 340 * 341 * @param decimal The BigDecimal value to round. 342 * @return Rounded BigDecimal value 343 * **/ 344 public static BigDecimal roundHalfEven(BigDecimal decimal) { 345 return decimal.setScale(0, BigDecimal.ROUND_HALF_EVEN); 346 } 347 348 /** Rounding mode to round towards "nearest neighbor" unless 349 * both neighbors are equidistant, in which case round 350 * "ceiling". 351 * Behaves as HALF_ROUND_DOWN if the decimal is positive and 352 * as HALF_ROUND_UP if the decimal is negative. 353 * <p> 354 * 355 * Note that there is no half floor rounding mode 356 * supported for BigDecimal values. This method uses a 357 * combination of the {@link BigDecimal#ROUND_HALF_UP} and 358 * {@link BigDecimal#ROUND_HALF_DOWN} to perform this 359 * new rounding mode. 360 * 361 * @param decimal The BigDecimal value to round. 362 * @return Rounded BigDecimal value 363 * **/ 364 public static BigDecimal roundHalfFloor(BigDecimal decimal) { 365 if (decimal.signum() == -1) { 366 return roundHalfUp(decimal); 367 } 368 return roundHalfDown(decimal); 369 } 370 371 /** Rounding mode to round towards "nearest neighbor" unless 372 * both neighbors are equidistant, in which case round up. 373 * Behaves as for ROUND_UP if the discarded fraction is ≥ .5; 374 * otherwise, behaves as for ROUND_DOWN. Note that this is the 375 * rounding mode that most of us were taught in grade school. 376 * Rounding mode to round towards zero. 377 * 378 * @see BigDecimal#ROUND_HALF_UP 379 * 380 * @param decimal The BigDecimal value to round. 381 * @return Rounded BigDecimal value 382 * **/ 383 public static BigDecimal roundHalfUp(BigDecimal decimal) { 384 return decimal.setScale(0, BigDecimal.ROUND_HALF_UP); 385 } 386 387 /** Rounding mode to round away from zero. 388 * Always increments the digit prior to a non-zero discarded fraction. 389 * Note that this rounding mode never decreases the magnitude of 390 * the calculated value. 391 * 392 * @see BigDecimal#ROUND_UP 393 * 394 * @param decimal The BigDecimal value to round. 395 * @return Rounded BigDecimal value 396 * **/ 397 public static BigDecimal roundUp(BigDecimal decimal) { 398 return decimal.setScale(0, BigDecimal.ROUND_UP); 399 } 400 401 /** Return the string representation of this rounding. 402 * @return A String. 403 */ 404 @Override 405 public String toString() { 406 return _name; 407 } 408 409 /////////////////////////////////////////////////////////////////// 410 //// public variables //// 411 // NOTE: It may seem strange that these inner classes are built this 412 // way instead of as anonymous classes... This code was copied from 413 // ptolemy.data.type.BaseType where an explanation that was valid 414 // for that usage may be found. 415 416 /** Singleton implementing ceiling rounding strategy. */ 417 public static final RoundCeiling CEILING = new RoundCeiling(); 418 419 /** Singleton implementing floor rounding strategy. */ 420 public static final RoundFloor FLOOR = new RoundFloor(); 421 422 /** Singleton implementing truncate rounding strategy. */ 423 public static final RoundFloor TRUNCATE = FLOOR; 424 425 /** Singleton implementing down rounding strategy. */ 426 public static final RoundDown DOWN = new RoundDown(); 427 428 /** Singleton implementing up rounding strategy. */ 429 public static final RoundUp UP = new RoundUp(); 430 431 /** Singleton implementing half down rounding strategy. */ 432 public static final RoundHalfDown HALF_DOWN = new RoundHalfDown(); 433 434 /** Singleton implementing half up rounding strategy. */ 435 public static final RoundHalfUp HALF_UP = new RoundHalfUp(); 436 437 /** Singleton implementing half even rounding strategy. */ 438 public static final RoundHalfEven HALF_EVEN = new RoundHalfEven(); 439 440 /** Singleton implementing convergent rounding strategy. */ 441 public static final RoundHalfEven CONVERGENT = HALF_EVEN; 442 443 /** Singleton implementing half ceiling rounding strategy. */ 444 public static final RoundHalfCeiling HALF_CEILING = new RoundHalfCeiling(); 445 446 /** Singleton implementing nearest rounding strategy. */ 447 public static final RoundHalfCeiling NEAREST = HALF_CEILING; 448 449 /** Singleton implementing half floor rounding strategy. */ 450 public static final RoundHalfFloor HALF_FLOOR = new RoundHalfFloor(); 451 452 // The following singleton classes are listed below (and out of 453 // alphabetic order) because they depend on the construction of 454 // other singleton objects before they can be defined. 455 456 /** Singleton implementing general rounding strategy. */ 457 public static final Rounding GENERAL = UP; 458 459 /** Singleton implementing unknown rounding strategy. */ 460 public static final Rounding UNKNOWN = UP; 461 462 /** Singleton implementing unnecessary rounding strategy. */ 463 public static final Rounding UNNECESSARY = HALF_UP; 464 465 /////////////////////////////////////////////////////////////////// 466 //// public inner classes //// 467 468 /** Rounding class implementing the round ceiling strategy. */ 469 public static class RoundCeiling extends Rounding { 470 private RoundCeiling() { 471 super("ceiling"); 472 } 473 474 @Override 475 public BigInteger round(BigDecimal dec) { 476 return roundCeiling(dec).toBigInteger(); 477 } 478 } 479 480 /** Rounding class implementing the round down strategy. */ 481 public static class RoundDown extends Rounding { 482 private RoundDown() { 483 super("down"); 484 } 485 486 @Override 487 public BigInteger round(BigDecimal dec) { 488 return roundDown(dec).toBigInteger(); 489 } 490 } 491 492 /** Rounding class implementing the round floor strategy. */ 493 public static class RoundFloor extends Rounding { 494 private RoundFloor() { 495 super("floor"); 496 _addRounding(this, "truncate"); 497 } 498 499 @Override 500 public BigInteger round(BigDecimal dec) { 501 return roundFloor(dec).toBigInteger(); 502 } 503 } 504 505 /** Rounding class implementing the round half ceiling strategy. */ 506 public static class RoundHalfCeiling extends Rounding { 507 private RoundHalfCeiling() { 508 super("half_ceiling"); 509 _addRounding(this, "nearest"); 510 _addRounding(this, "round"); // For compatibility 511 } 512 513 @Override 514 public BigInteger round(BigDecimal dec) { 515 return roundHalfCeiling(dec).toBigInteger(); 516 } 517 } 518 519 /** Rounding class implementing the round half down strategy. */ 520 public static class RoundHalfDown extends Rounding { 521 private RoundHalfDown() { 522 super("half_down"); 523 } 524 525 @Override 526 public BigInteger round(BigDecimal dec) { 527 return roundHalfDown(dec).toBigInteger(); 528 } 529 } 530 531 /** Rounding class implementing the round half even strategy. */ 532 public static class RoundHalfEven extends Rounding { 533 private RoundHalfEven() { 534 super("half_even"); 535 _addRounding(this, "convergent"); 536 } 537 538 @Override 539 public BigInteger round(BigDecimal dec) { 540 return roundHalfEven(dec).toBigInteger(); 541 } 542 } 543 544 /** Rounding class implementing the round half floor strategy. */ 545 public static class RoundHalfFloor extends Rounding { 546 private RoundHalfFloor() { 547 super("half_floor"); 548 } 549 550 @Override 551 public BigInteger round(BigDecimal dec) { 552 return roundHalfFloor(dec).toBigInteger(); 553 } 554 } 555 556 /** Rounding class implementing the round half up strategy. */ 557 public static class RoundHalfUp extends Rounding { 558 private RoundHalfUp() { 559 super("half_up"); 560 _addRounding(this, "unnecessary"); 561 } 562 563 @Override 564 public BigInteger round(BigDecimal dec) { 565 return roundHalfUp(dec).toBigInteger(); 566 } 567 } 568 569 /** Rounding class implementing the round up strategy. */ 570 public static class RoundUp extends Rounding { 571 private RoundUp() { 572 super("up"); 573 _addRounding(this, "general"); 574 _addRounding(this, "unknown"); 575 } 576 577 @Override 578 public BigInteger round(BigDecimal dec) { 579 return roundUp(dec).toBigInteger(); 580 } 581 } 582 583 /////////////////////////////////////////////////////////////////// 584 //// protected constructor //// 585 586 /** Construct a Rounding object with the given String name. 587 * This name is used for finding a Rounding object at a later 588 * time (@see #forName(String)). This constructor 589 * is protected to make a type safe enumeration. 590 * 591 * @param name The String name to give this Rounding strategy. 592 * 593 */ 594 protected Rounding(String name) { 595 _name = name; 596 _addRounding(this, name); 597 } 598 599 /////////////////////////////////////////////////////////////////// 600 //// protected methods //// 601 602 /////////////////////////////////////////////////////////////////// 603 //// package private method //// 604 605 // Add entries in this class to index the given name to 606 // the given rounding type. 607 static void _addRounding(Rounding type, String name) { 608 // Because the private variables are below the public variables 609 // that call this initializer, 610 // it doesn't work to initialize this statically. 611 if (_nameToRounding == null) { 612 _nameToRounding = new HashMap(); 613 } 614 615 _nameToRounding.put(name, type); 616 } 617 618 /////////////////////////////////////////////////////////////////// 619 //// private variables //// 620 621 /** The name of the rounding mode. */ 622 private String _name; 623 624 /** A map from rounding type name to the rounding type for all rounding 625 * types. 626 */ 627 private static Map _nameToRounding; 628 629}