001/** 002 * The MIT License (MIT) 003 * 004 * Copyright (c) 2015-2016 decimal4j (tools4j), Marco Terzer 005 * 006 * Permission is hereby granted, free of charge, to any person obtaining a copy 007 * of this software and associated documentation files (the "Software"), to deal 008 * in the Software without restriction, including without limitation the rights 009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 010 * copies of the Software, and to permit persons to whom the Software is 011 * furnished to do so, subject to the following conditions: 012 * 013 * The above copyright notice and this permission notice shall be included in all 014 * copies or substantial portions of the Software. 015 * 016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 022 * SOFTWARE. 023 */ 024package org.decimal4j.truncate; 025 026import java.math.RoundingMode; 027import java.util.Collections; 028import java.util.EnumSet; 029import java.util.Set; 030 031/** 032 * Defines the same constants as {@link RoundingMode} and implements the 033 * functionality to actually perform such rounding. 034 */ 035public enum DecimalRounding { 036 037 /** 038 * Rounding mode to round away from zero. Always increments the digit prior 039 * to a non-zero discarded fraction. Note that this rounding mode never 040 * decreases the magnitude of the calculated value. 041 * 042 * @see RoundingMode#UP 043 */ 044 UP { 045 @Override 046 public final RoundingMode getRoundingMode() { 047 return RoundingMode.UP; 048 } 049 @Override 050 public final int calculateRoundingIncrement(int sgn, long truncatedValue, TruncatedPart truncatedPart) { 051 if (truncatedPart.isGreaterThanZero()) { 052 return sgn; 053 } 054 return 0; 055 } 056 }, 057 058 /** 059 * Rounding mode to round towards zero. Never increments the digit prior to 060 * a discarded fraction (i.e., truncates). Note that this rounding mode 061 * never increases the magnitude of the calculated value. 062 * 063 * @see RoundingMode#DOWN 064 */ 065 DOWN { 066 @Override 067 public final RoundingMode getRoundingMode() { 068 return RoundingMode.DOWN; 069 } 070 @Override 071 public final int calculateRoundingIncrement(int sgn, long truncatedValue, TruncatedPart truncatedPart) { 072 return 0; 073 } 074 }, 075 076 /** 077 * Rounding mode to round towards positive infinity. If the result is 078 * positive, behaves as for {@code RoundingMode.UP}; if negative, behaves as 079 * for {@code RoundingMode.DOWN}. Note that this rounding mode never 080 * decreases the calculated value. 081 * 082 * @see RoundingMode#CEILING 083 */ 084 CEILING { 085 @Override 086 public final RoundingMode getRoundingMode() { 087 return RoundingMode.CEILING; 088 } 089 @Override 090 public final int calculateRoundingIncrement(int sgn, long truncatedValue, TruncatedPart truncatedPart) { 091 if (sgn > 0) { 092 if (truncatedPart.isGreaterThanZero()) { 093 return 1; 094 } 095 } 096 return 0; 097 } 098 }, 099 100 /** 101 * Rounding mode to round towards negative infinity. If the result is 102 * positive, behave as for {@code RoundingMode.DOWN}; if negative, behave as 103 * for {@code RoundingMode.UP}. Note that this rounding mode never increases 104 * the calculated value. 105 * 106 * @see RoundingMode#FLOOR 107 */ 108 FLOOR { 109 @Override 110 public final RoundingMode getRoundingMode() { 111 return RoundingMode.FLOOR; 112 } 113 @Override 114 public final int calculateRoundingIncrement(int sgn, long truncatedValue, TruncatedPart truncatedPart) { 115 if (sgn < 0) { 116 if (truncatedPart.isGreaterThanZero()) { 117 return -1; 118 } 119 } 120 return 0; 121 } 122 }, 123 124 /** 125 * Rounding mode to round towards {@literal "nearest neighbor"} unless both 126 * neighbors are equidistant, in which case round up. Behaves as for 127 * {@code RoundingMode.UP} if the discarded fraction is ≥ 0.5; otherwise, 128 * behaves as for {@code RoundingMode.DOWN}. Note that this is the rounding 129 * mode commonly taught at school. 130 * 131 * @see RoundingMode#HALF_UP 132 */ 133 HALF_UP { 134 @Override 135 public final RoundingMode getRoundingMode() { 136 return RoundingMode.HALF_UP; 137 } 138 @Override 139 public final int calculateRoundingIncrement(int sgn, long truncatedValue, TruncatedPart truncatedPart) { 140 if (truncatedPart.isGreaterEqualHalf()) { 141 return sgn; 142 } 143 return 0; 144 } 145 }, 146 147 /** 148 * Rounding mode to round towards {@literal "nearest neighbor"} unless both 149 * neighbors are equidistant, in which case round down. Behaves as for 150 * {@code RoundingMode.UP} if the discarded fraction is > 0.5; otherwise, 151 * behaves as for {@code RoundingMode.DOWN}. 152 * 153 * @see RoundingMode#HALF_DOWN 154 */ 155 HALF_DOWN { 156 @Override 157 public final RoundingMode getRoundingMode() { 158 return RoundingMode.HALF_DOWN; 159 } 160 @Override 161 public final int calculateRoundingIncrement(int sgn, long truncatedValue, TruncatedPart truncatedPart) { 162 if (truncatedPart.isGreaterThanHalf()) { 163 return sgn; 164 } 165 return 0; 166 } 167 }, 168 169 /** 170 * Rounding mode to round towards the {@literal "nearest neighbor"} unless 171 * both neighbors are equidistant, in which case, round towards the even 172 * neighbor. Behaves as for {@code RoundingMode.HALF_UP} if the digit to the 173 * left of the discarded fraction is odd; behaves as for 174 * {@code RoundingMode.HALF_DOWN} if it's even. Note that this is the 175 * rounding mode that statistically minimizes cumulative error when applied 176 * repeatedly over a sequence of calculations. It is sometimes known as 177 * {@literal "Banker's rounding,"} and is chiefly used in the USA. This 178 * rounding mode is analogous to the rounding policy used for {@code float} 179 * and {@code double} arithmetic in Java. 180 * 181 * @see RoundingMode#HALF_EVEN 182 */ 183 HALF_EVEN { 184 @Override 185 public final RoundingMode getRoundingMode() { 186 return RoundingMode.HALF_EVEN; 187 } 188 @Override 189 public final int calculateRoundingIncrement(int sgn, long truncatedValue, TruncatedPart truncatedPart) { 190 if (truncatedPart.isGreaterEqualHalf()) { 191 if (truncatedPart.isGreaterThanHalf() | ((truncatedValue & 0x1) != 0)) { 192 return sgn; 193 } 194 } 195 return 0; 196 } 197 }, 198 199 /** 200 * Rounding mode to assert that the requested operation has an exact result, 201 * hence no rounding is necessary. If this rounding mode is specified on an 202 * operation that yields an inexact result, an {@code ArithmeticException} 203 * is thrown. 204 * 205 * @see RoundingMode#UNNECESSARY 206 */ 207 UNNECESSARY { 208 @Override 209 public final RoundingMode getRoundingMode() { 210 return RoundingMode.UNNECESSARY; 211 } 212 @Override 213 public final int calculateRoundingIncrement(int sgn, long truncatedValue, TruncatedPart truncatedPart) { 214 if (truncatedPart.isGreaterThanZero()) { 215 throw new ArithmeticException("Rounding necessary"); 216 } 217 return 0; 218 } 219 }; 220 221 /** 222 * Immutable set with all values of this enum. Avoids object creation in 223 * contrast to {@link #values()}. 224 */ 225 public static final Set<DecimalRounding> VALUES = Collections.unmodifiableSet(EnumSet.allOf(DecimalRounding.class)); 226 227 /** 228 * Returns the {@link RoundingMode} associated with this decimal rounding 229 * constant. 230 * 231 * @return the corresponding rounding mode 232 */ 233 abstract public RoundingMode getRoundingMode(); 234 235 /** 236 * Returns the rounding increment appropriate for this decimal rounding. The 237 * returned value is one of -1, 0 or 1. 238 * 239 * @param sign 240 * the sign of the total value, either +1 or -1; determines the 241 * result value if rounded 242 * @param truncatedValue 243 * the truncated result before rounding is applied 244 * @param truncatedPart 245 * classification of the trunctated part of the value 246 * @return the value to add to {@code truncatedValue} to get the rounded 247 * result, one of -1, 0 or 1 248 */ 249 abstract public int calculateRoundingIncrement(int sign, long truncatedValue, TruncatedPart truncatedPart); 250 251 /** 252 * Returns the decimal rounding constant for the given rounding mode. 253 * 254 * @param roundingMode 255 * the rounding mode 256 * @return the constant corresponding to the given rounding mode 257 */ 258 public static final DecimalRounding valueOf(RoundingMode roundingMode) { 259 return ByRoundingMode.VALUES_BY_ROUNDING_MODE_ORDINAL[roundingMode.ordinal()]; 260 } 261 262 private static class ByRoundingMode { 263 private static final DecimalRounding[] VALUES_BY_ROUNDING_MODE_ORDINAL = sortByRoundingModeOrdinal(); 264 265 private static final DecimalRounding[] sortByRoundingModeOrdinal() { 266 final DecimalRounding[] sorted = new DecimalRounding[VALUES.size()]; 267 for (final DecimalRounding dr : VALUES) { 268 sorted[dr.getRoundingMode().ordinal()] = dr; 269 } 270 return sorted; 271 } 272 } 273}