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.api.DecimalArithmetic; 027import org.decimal4j.scale.Scale0f; 028import org.decimal4j.scale.ScaleMetrics; 029import org.decimal4j.scale.Scales; 030import org.decimal4j.truncate.DecimalRounding; 031 032/** 033 * Provides static methods to calculate additions. 034 */ 035final class Add { 036 037 /** 038 * Calculates unchecked unrounded addition of a long value and an unscaled 039 * value with the given scale. 040 * 041 * @param lValue 042 * the long value 043 * @param unscaled 044 * the unscaled value 045 * @param scale 046 * the scale of the second value 047 * @return the addition result without rounding and without overflow checks 048 */ 049 public static final long addLongUnscaled(long lValue, long unscaled, int scale) { 050 return addUnscaledUnscaled(Scale0f.INSTANCE, lValue, unscaled, scale); 051 } 052 053 /** 054 * Calculates unchecked rounded addition of a long value and an unscaled 055 * value with the given scale. 056 * 057 * @param rounding 058 * the rounding to apply 059 * @param lValue 060 * the long value 061 * @param unscaled 062 * the unscaled value 063 * @param scale 064 * the scale of the second value 065 * @return the addition result with rounding but without overflow checks 066 */ 067 public static final long addLongUnscaled(DecimalRounding rounding, long lValue, long unscaled, int scale) { 068 return addUnscaledUnscaled(Scale0f.INSTANCE, rounding, lValue, unscaled, scale); 069 } 070 071 /** 072 * Calculates unchecked addition of an unscaled value and a long value. 073 * 074 * @param arith 075 * the arithmetic associated with the first value 076 * @param uDecimal 077 * the unscaled value 078 * @param lValue 079 * the long value 080 * @return the addition result without overflow checks 081 */ 082 public static final long addUnscaledLong(DecimalArithmetic arith, long uDecimal, long lValue) { 083 return uDecimal + Pow10.multiplyByPowerOf10(lValue, arith.getScale()); 084 } 085 086 /** 087 * Calculates checked addition of an unscaled value and a long value. 088 * 089 * @param arith 090 * the arithmetic associated with the first value 091 * @param uDecimal 092 * the unscaled value 093 * @param lValue 094 * the long value 095 * @return the addition result performed with overflow checks 096 */ 097 public static final long addUnscaledLongChecked(DecimalArithmetic arith, long uDecimal, long lValue) { 098 final int scale = arith.getScale(); 099 if (lValue == 0 | scale == 0) { 100 return arith.add(uDecimal, lValue); 101 } 102 try { 103 return addForNegativeScaleDiff(arith, uDecimal, lValue, -scale); 104 } catch (ArithmeticException e) { 105 throw Exceptions.newArithmeticExceptionWithCause("Overflow: " + arith.toString(uDecimal) + " + " + lValue, e); 106 } 107 } 108 109 /** 110 * Calculates unchecked unrounded addition of an unscaled value and another 111 * unscaled value with the given {@code scaleMetrics} and {@code scale}, 112 * respectively. 113 * 114 * @param scaleMetrics 115 * the scaleMetrics associated with the first value 116 * @param uDecimal 117 * the first unscaled value 118 * @param unscaled 119 * the second unscaled value 120 * @param scale 121 * the scale of the second value 122 * @return the addition result without rounding and without overflow checks 123 */ 124 public static final long addUnscaledUnscaled(ScaleMetrics scaleMetrics, long uDecimal, long unscaled, int scale) { 125 if (scale > Scales.MAX_SCALE) { 126 throw new IllegalArgumentException("Illegal scale, must be <=" + Scales.MAX_SCALE + " but was " + scale); 127 } 128 final int scaleDiff = scale - scaleMetrics.getScale(); 129 if (unscaled == 0 | scaleDiff == 0) { 130 return uDecimal + unscaled; 131 } else if (scaleDiff < 0) { 132 return uDecimal + Pow10.divideByPowerOf10(unscaled, scaleDiff);//multiplication 133 } 134 return addForPositiveScaleDiff(uDecimal, unscaled, scaleDiff); 135 } 136 137 /** 138 * Calculates unchecked rounded addition of an unscaled value and another 139 * unscaled value with the given {@code scaleMetrics} and {@code scale}, 140 * respectively. 141 * 142 * @param scaleMetrics 143 * the scaleMetrics associated with the first value 144 * @param rounding 145 * the rounding to apply 146 * @param uDecimal 147 * the first unscaled value 148 * @param unscaled 149 * the second unscaled value 150 * @param scale 151 * the scale of the second value 152 * @return the addition result with rounding but without overflow checks 153 */ 154 public static final long addUnscaledUnscaled(ScaleMetrics scaleMetrics, DecimalRounding rounding, long uDecimal, long unscaled, int scale) { 155 if (scale > Scales.MAX_SCALE) { 156 throw new IllegalArgumentException("Illegal scale, must be <=" + Scales.MAX_SCALE + " but was " + scale); 157 } 158 final int scaleDiff = scale - scaleMetrics.getScale(); 159 if (unscaled == 0 | scaleDiff == 0) { 160 return uDecimal + unscaled; 161 } else if (scaleDiff < 0) { 162 return uDecimal + Pow10.divideByPowerOf10(unscaled, scaleDiff);//multiplication 163 } 164 //scale > 0 165 return addForPositiveScaleDiff(rounding, uDecimal, unscaled, scaleDiff); 166 } 167 168 /** 169 * Calculates checked unrounded addition of an unscaled value and another 170 * unscaled value with the given {@code scaleMetrics} and {@code scale}, 171 * respectively. 172 * 173 * @param arith 174 * the arithmetic associated with the first value 175 * @param uDecimal 176 * the first unscaled value 177 * @param unscaled 178 * the second unscaled value 179 * @param scale 180 * the scale of the second value 181 * @return the addition result without rounding but with overflow checks 182 */ 183 public static final long addUnscaledUnscaledChecked(DecimalArithmetic arith, long uDecimal, long unscaled, int scale) { 184 if (scale > Scales.MAX_SCALE) { 185 throw new IllegalArgumentException("Illegal scale, must be <=" + Scales.MAX_SCALE + " but was " + scale); 186 } 187 final int scaleDiff = scale - arith.getScale(); 188 if (unscaled == 0 | scaleDiff == 0) { 189 return arith.add(uDecimal, unscaled); 190 } else if (scaleDiff < 0) { 191 try { 192 return addForNegativeScaleDiff(arith, uDecimal, unscaled, scaleDiff); 193 } catch (ArithmeticException e) { 194 throw Exceptions.newArithmeticExceptionWithCause("Overflow: " + arith.toString(uDecimal) + " + " + unscaled + "*10^" + (-scale), e); 195 } 196 } 197 final long sum = addForPositiveScaleDiff(uDecimal, unscaled, scaleDiff); 198 if (!Checked.isAddOverflow(uDecimal, unscaled, sum)) { 199 return sum; 200 } 201 throw new ArithmeticException("Overflow: " + arith.toString(uDecimal) + " + " + unscaled + "*10^" + (-scale) + "=" + sum); 202 } 203 204 /** 205 * Calculates checked rounded addition of an unscaled value and another 206 * unscaled value with the given {@code arith} and {@code scale}, 207 * respectively. 208 * 209 * @param arith 210 * the arithmetic associated with the first value 211 * @param rounding 212 * the rounding to apply 213 * @param uDecimal 214 * the first unscaled value 215 * @param unscaled 216 * the second unscaled value 217 * @param scale 218 * the scale of the second value 219 * @return the addition result with rounding and overflow checks 220 */ 221 public static final long addUnscaledUnscaledChecked(DecimalArithmetic arith, DecimalRounding rounding, long uDecimal, long unscaled, int scale) { 222 if (scale > Scales.MAX_SCALE) { 223 throw new IllegalArgumentException("Illegal scale, must be <=" + Scales.MAX_SCALE + " but was " + scale); 224 } 225 final int scaleDiff = scale - arith.getScale(); 226 if (unscaled == 0 | scaleDiff == 0) { 227 return arith.add(uDecimal, unscaled); 228 } else if (scaleDiff < 0) { 229 try { 230 return addForNegativeScaleDiff(arith, uDecimal, unscaled, scaleDiff); 231 } catch (ArithmeticException e) { 232 throw Exceptions.newArithmeticExceptionWithCause("Overflow: " + arith.toString(uDecimal) + " + " + unscaled + "*10^" + (-scale), e); 233 } 234 } 235 final long sum = addForPositiveScaleDiff(rounding, uDecimal, unscaled, scaleDiff); 236 if (!Checked.isAddOverflow(uDecimal, unscaled, sum)) { 237 return sum; 238 } 239 throw new ArithmeticException("Overflow: " + arith.toString(uDecimal) + " + " + unscaled + "*10^" + (-scale) + "=" + sum); 240 } 241 242 /** 243 * Calculates unchecked unrounded addition of an unscaled value and another 244 * unscaled value with the given {@code scaleDiff=scale2-scale1 > 0}. 245 * 246 * @param uDecimal 247 * the first unscaled value 248 * @param unscaled 249 * the second unscaled value 250 * @param scaleDiff 251 * scale2 - scale1, must be positive 252 * @return the addition result without rounding and without overflow checks 253 */ 254 //PRECONDITION: scaleDiff > 0 255 private static final long addForPositiveScaleDiff(long uDecimal, long unscaled, int scaleDiff) { 256 //scaleDiff > 0 257 final ScaleMetrics diffMetrics = Scales.getScaleMetrics(scaleDiff); 258 final long trunc = diffMetrics.divideByScaleFactor(unscaled); 259 final long sum = uDecimal + trunc; 260 if (uDecimal == 0 | sum == 0 | (uDecimal ^ unscaled) >= 0 | (sum ^ unscaled) >= 0) { 261 return sum; 262 } 263 final long remainder = unscaled - diffMetrics.multiplyByScaleFactor(trunc); 264 return sum + Long.signum(remainder); 265 } 266 267 /** 268 * Calculates unchecked rounded addition of an unscaled value and another 269 * unscaled value with the given {@code scaleDiff=scale2-scale1 > 0}. 270 * 271 * @param rounding 272 * the rounding to apply 273 * @param uDecimal 274 * the first unscaled value 275 * @param unscaled 276 * the second unscaled value 277 * @param scaleDiff 278 * scale2 - scale1, must be positive 279 * @return the addition result with rounding but without overflow checks 280 */ 281 //PRECONDITION: scaleDiff > 0 282 private static final long addForPositiveScaleDiff(DecimalRounding rounding, long uDecimal, long unscaled, int scaleDiff) { 283 //scaleDiff > 0 284 final ScaleMetrics diffMetrics = Scales.getScaleMetrics(scaleDiff); 285 final long trunc = diffMetrics.divideByScaleFactor(unscaled); 286 final long remainder = unscaled - diffMetrics.multiplyByScaleFactor(trunc); 287 final long sum = uDecimal + trunc; 288 if (uDecimal == 0 | sum == 0 | (uDecimal ^ unscaled) >= 0 | (sum ^ unscaled) >= 0) { 289 return sum + Rounding.calculateRoundingIncrement(rounding, sum, remainder, diffMetrics.getScaleFactor()); 290 } 291 return sum + Rounding.calculateRoundingIncrement(RoundingInverse.ADDITIVE_REVERSION.invert(rounding), sum, remainder, diffMetrics.getScaleFactor()); 292 } 293 294 /** 295 * Calculates checked addition of an unscaled value and another 296 * unscaled value with the given {@code scaleDiff = scal2 - scale1 > 0} which must be negative, such that the added 297 * value can be rescaled through multiplication. 298 * 299 * @param arith 300 * the arithmetic associated with the first value 301 * @param uDecimal 302 * the first unscaled value 303 * @param unscaled 304 * the second unscaled value 305 * @param scaleDiff 306 * the scale of the second value 307 * @return the addition result with overflow checks 308 */ 309 //PRECONDITION: scaleDiff < 0 310 private static final long addForNegativeScaleDiff(DecimalArithmetic arith, long uDecimal, long unscaled, int scaleDiff) { 311 //NOTE: multiplication by power of 10 may lead to an overflow but the result may still be valid if signs are opposite 312 // --> therefore we multiply only half of the value with pow10 and add it twice 313 // --> then we add the remainder 1 (x pow10) if the value was odd (again in halves to avoid overflow) 314 final long half = Pow10.divideByPowerOf10Checked(arith, unscaled / 2, scaleDiff);//multiplication; 315 final long halfReminder = ((unscaled & 0x1) == 0) ? 0 : Pow10.divideByPowerOf10Checked(arith, unscaled > 0 ? 5 : -5, scaleDiff + 1); 316 long result = uDecimal; 317 result = arith.add(result, half); 318 result = arith.add(result, half); 319 result = arith.add(result, halfReminder); 320 result = arith.add(result, halfReminder); 321 return result; 322 } 323 324 // no instances 325 private Add() { 326 super(); 327 } 328}