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.arithmetic; 025 026import org.decimal4j.truncate.DecimalRounding; 027import org.decimal4j.truncate.TruncatedPart; 028 029/** 030 * Utility class to calculate rounding increments in different situations; 031 * utilizes functionality provided by {@link DecimalRounding} and 032 * {@link TruncatedPart}. 033 */ 034final class Rounding { 035 036 /** 037 * Returns the rounding increment appropriate for the specified 038 * {@code rounding}. The returned value is one of -1, 0 or 1. 039 * 040 * @param rounding 041 * the rounding mode to apply 042 * @param sign 043 * the sign of the total value, either +1 or -1; determines the 044 * result value if rounded 045 * @param truncatedValue 046 * the truncated result before rounding is applied (only used for 047 * HALF_EVEN rounding) 048 * @param firstTruncatedDigit 049 * the first truncated digit, must be in {@code [0, 1, ..., 9]} 050 * @param zeroAfterFirstTruncatedDigit 051 * true if all truncated digits after the first truncated digit 052 * are zero, and false otherwise 053 * @return the value to add to {@code truncatedValue} to get the rounded 054 * result, one of -1, 0 or 1 055 */ 056 public static final int calculateRoundingIncrement(DecimalRounding rounding, int sign, long truncatedValue, int firstTruncatedDigit, boolean zeroAfterFirstTruncatedDigit) { 057 return rounding.calculateRoundingIncrement(sign, truncatedValue, TruncatedPart.valueOf(firstTruncatedDigit, zeroAfterFirstTruncatedDigit)); 058 } 059 060 /** 061 * Returns the rounding increment appropriate for the specified 062 * {@code rounding} given the remaining truncated digits truncated by a 063 * given divisor. The returned value is one of -1, 0 or 1. 064 * 065 * @param rounding 066 * the rounding mode to apply 067 * @param truncatedValue 068 * the truncated result before rounding is applied (only used for 069 * HALF_EVEN rounding) 070 * @param truncatedDigits 071 * the truncated part, it most hold that 072 * {@code abs(truncatedDigits) < abs(divisor)} 073 * @param divisor 074 * the divisor that led to the truncated digits 075 * @return the value to add to {@code truncatedValue} to get the rounded 076 * result, one of -1, 0 or 1 077 */ 078 public static final int calculateRoundingIncrementForDivision(DecimalRounding rounding, long truncatedValue, long truncatedDigits, long divisor) { 079 if (truncatedDigits == 0) { 080 return 0; 081 } 082 final TruncatedPart truncatedPart = truncatedPartFor(Math.abs(truncatedDigits), Math.abs(divisor)); 083 return rounding.calculateRoundingIncrement(Long.signum(truncatedDigits ^ divisor), truncatedValue, truncatedPart); 084 } 085 086 /** 087 * Returns the rounding increment appropriate for the specified 088 * {@code rounding} given the remaining truncated digits truncated by modulo 089 * one. The returned value is one of -1, 0 or 1. 090 * 091 * @param rounding 092 * the rounding mode to apply 093 * @param truncatedValue 094 * the truncated result before rounding is applied (only used for 095 * HALF_EVEN rounding) 096 * @param truncatedDigits 097 * the truncated part of a double, must be {@code >-one} and 098 * {@code <one} 099 * @param one 100 * the value representing 1 which is {@code 10^scale}, must be 101 * {@code >= 10} 102 * @return the value to add to {@code truncatedValue} to get the rounded 103 * result, one of -1, 0 or 1 104 */ 105 public static final int calculateRoundingIncrement(DecimalRounding rounding, long truncatedValue, long truncatedDigits, long one) { 106 if (truncatedDigits == 0) { 107 return 0; 108 } 109 final TruncatedPart truncatedPart = truncatedPartFor(Math.abs(truncatedDigits), one); 110 return rounding.calculateRoundingIncrement(Long.signum(truncatedDigits), truncatedValue, truncatedPart); 111 } 112 113 /** 114 * Returns a truncated part constant given a non-negative remainder 115 * resulting from a division by the given non-negative divisor. 116 * 117 * @param nonNegativeRemainder 118 * the remainder part, not negative and 119 * {@code nonNegativeRemainder < nonNegativeDivisor} 120 * @param nonNegativeDivisor 121 * the divisor, not negative or LONG.MIN_VALUE --- the latter 122 * equal to {@code abs(Long.MIN_VALUE)} 123 * @return the truncated part constant equivalent to the given arguments 124 */ 125 public static final TruncatedPart truncatedPartFor(long nonNegativeRemainder, long nonNegativeDivisor) { 126 if (nonNegativeRemainder == 0) { 127 return TruncatedPart.ZERO; 128 } 129 final long halfNonNegativeDivisor = nonNegativeDivisor >>> 1; 130 //NOTE: halfNonNegativeDivisor cannot be zero, because if it was 1 then nonNegativeRemainder was 0 131 132 if (halfNonNegativeDivisor < nonNegativeRemainder) { 133 return TruncatedPart.GREATER_THAN_HALF; 134 } 135 if ((nonNegativeDivisor & 0x1) == 0 & halfNonNegativeDivisor == nonNegativeRemainder) { 136 return TruncatedPart.EQUAL_TO_HALF; 137 } 138 return TruncatedPart.LESS_THAN_HALF_BUT_NOT_ZERO; 139 } 140 141 /** 142 * Returns a truncated part constant given a non-negative remainder 143 * resulting from a division by 10^19. 144 * 145 * @param remainder 146 * the remainder part 147 * @return the truncated part constant equivalent to the given arguments 148 */ 149 public static final TruncatedPart truncatedPartForScale19(long remainder) { 150 if (remainder == 0) { 151 return TruncatedPart.ZERO; 152 } 153 if (5000000000000000000L > remainder & remainder > -5000000000000000000L) { 154 return TruncatedPart.LESS_THAN_HALF_BUT_NOT_ZERO; 155 } 156 if (remainder == 5000000000000000000L | remainder == -5000000000000000000L) { 157 return TruncatedPart.EQUAL_TO_HALF; 158 } 159 return TruncatedPart.GREATER_THAN_HALF; 160 } 161 162 /** 163 * Returns a truncated part constant given a non-negative remainder 164 * resulting from a division by 2^n 165 * 166 * @param remainder 167 * the remainder part 168 * @param n 169 * the power of 2 of the divisor, {@code n > 0} 170 * @return the truncated part constant equivalent to the given arguments 171 */ 172 public static final TruncatedPart truncatedPartFor2powN(long remainder, int n) { 173 if (n < 63) { 174 return truncatedPartFor(remainder, 1L << n); 175 } else if (n == 63) { 176 return truncatedPartFor2pow63(remainder); 177 } else if (n == 64) { 178 return truncatedPartFor2pow64(remainder); 179 } else { 180 return remainder == 0 ? TruncatedPart.ZERO : TruncatedPart.LESS_THAN_HALF_BUT_NOT_ZERO; 181 } 182 } 183 184 /** 185 * Returns a truncated part constant given a non-negative 128 bit remainder 186 * resulting from a division by 2^n 187 * 188 * @param hRemainder 189 * the high bits of the remainder part 190 * @param lRemainder 191 * the low bits of the remainder part 192 * @param n 193 * the power of 2 of the divisor, {@code n > 0} 194 * @return the truncated part constant equivalent to the given arguments 195 */ 196 public static final TruncatedPart truncatedPartFor2powN(long hRemainder, long lRemainder, int n) { 197 if (hRemainder == 0) { 198 return truncatedPartFor2powN(lRemainder, n); 199 } 200 final TruncatedPart hPart = truncatedPartFor2powN(hRemainder, n - Long.SIZE); 201 switch (hPart) { 202 case ZERO: 203 return lRemainder == 0 ? TruncatedPart.ZERO : TruncatedPart.LESS_THAN_HALF_BUT_NOT_ZERO; 204 case LESS_THAN_HALF_BUT_NOT_ZERO: 205 return hPart; 206 case EQUAL_TO_HALF: 207 return lRemainder == 0 ? TruncatedPart.EQUAL_TO_HALF: TruncatedPart.GREATER_THAN_HALF; 208 case GREATER_THAN_HALF: 209 return hPart; 210 default: 211 throw new RuntimeException("internal error: unsupported truncated part: " + hPart); 212 } 213 } 214 215 /** 216 * Returns a truncated part constant given a non-negative remainder 217 * resulting from a division by 2^63 218 * 219 * @param remainder 220 * the remainder part 221 * @return the truncated part constant equivalent to the given arguments 222 */ 223 public static final TruncatedPart truncatedPartFor2pow63(long remainder) { 224 if (remainder == 0) { 225 return TruncatedPart.ZERO; 226 } 227 if ((1L << 62) > remainder & remainder > -(1L << 62)) { 228 return TruncatedPart.LESS_THAN_HALF_BUT_NOT_ZERO; 229 } 230 if (remainder == (1L << 62) | remainder == -(1L << 62)) { 231 return TruncatedPart.EQUAL_TO_HALF; 232 } 233 return TruncatedPart.GREATER_THAN_HALF; 234 } 235 236 /** 237 * Returns a truncated part constant given a non-negative remainder 238 * resulting from a division by 2^64 239 * 240 * @param remainder 241 * the remainder part 242 * @return the truncated part constant equivalent to the given arguments 243 */ 244 public static final TruncatedPart truncatedPartFor2pow64(long remainder) { 245 if (remainder == 0) { 246 return TruncatedPart.ZERO; 247 } 248 if (remainder == 0x8000000000000000L) { 249 return TruncatedPart.EQUAL_TO_HALF; 250 } 251 if (remainder < 0) { 252 return TruncatedPart.GREATER_THAN_HALF; 253 } 254 return TruncatedPart.LESS_THAN_HALF_BUT_NOT_ZERO; 255 } 256 257 // no instances 258 private Rounding() { 259 super(); 260 } 261}