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.truncate.DecimalRounding; 028import org.decimal4j.truncate.OverflowMode; 029import org.decimal4j.truncate.TruncatedPart; 030 031/** 032 * Provides methods for left and right shifts. 033 */ 034final class Shift { 035 036 /** 037 * Performs a shift left operation applying the given rounding mode if 038 * rounding is necessary. Overflows are siltently truncated. 039 * 040 * @param rounding 041 * the rounding to apply for negative position (i.e. a right 042 * shift) 043 * @param uDecimal 044 * the value to shift 045 * @param positions 046 * the positions to shift 047 * @return {@code round(uDecimal << positions)} 048 */ 049 public static final long shiftLeft(DecimalRounding rounding, long uDecimal, int positions) { 050 if (positions >= 0) { 051 return positions < Long.SIZE ? uDecimal << positions : 0; 052 } 053 // one shift missing for (-Integer.MIN_VALUE) but does not matter as 054 // result is always between 0 (incl) and 0.5 (excl) 055 return shiftRight(rounding, uDecimal, -positions > 0 ? -positions : Integer.MAX_VALUE); 056 } 057 058 /** 059 * Performs a shift right operation applying the given rounding mode if 060 * rounding is necessary. Overflows are siltently truncated. 061 * 062 * @param rounding 063 * the rounding to apply if necessary 064 * @param uDecimal 065 * the value to shift 066 * @param positions 067 * the positions to shift 068 * @return {@code round(uDecimal >> positions)} 069 */ 070 public static final long shiftRight(DecimalRounding rounding, long uDecimal, int positions) { 071 if (uDecimal == 0 | positions == 0) { 072 return uDecimal; 073 } 074 if (positions >= 0) { 075 if (rounding == DecimalRounding.FLOOR) { 076 return positions < Long.SIZE ? uDecimal >> positions : (uDecimal >= 0 ? 0 : -1); 077 } 078 if (positions < Long.SIZE) { 079 final long truncated = uDecimal >= 0 ? (uDecimal >>> positions) : -(-uDecimal >>> positions); 080 final long remainder = uDecimal - (truncated << positions); 081 final TruncatedPart truncatedPart = positions == 63 ? Rounding.truncatedPartFor2pow63(remainder) 082 : Rounding.truncatedPartFor(Math.abs(remainder), 1L << positions); 083 return truncated + rounding.calculateRoundingIncrement(Long.signum(uDecimal), truncated, truncatedPart); 084 } 085 if (positions == Long.SIZE) { 086 return rounding.calculateRoundingIncrement(Long.signum(uDecimal), 0, 087 Rounding.truncatedPartFor2pow64(Math.abs(uDecimal))); 088 } 089 return rounding.calculateRoundingIncrement(Long.signum(uDecimal), 0, 090 TruncatedPart.LESS_THAN_HALF_BUT_NOT_ZERO); 091 } 092 return positions > -Long.SIZE ? uDecimal << -positions : 0; 093 } 094 095 /** 096 * Performs a shift left operation applying the given rounding mode if 097 * rounding is necessary. Throws an exception if an overflow occurs. 098 * 099 * @param arith 100 * the arithmetic associated with the shifted value 101 * @param rounding 102 * the rounding to apply for negative position (i.e. a right 103 * shift) 104 * @param uDecimal 105 * the value to shift 106 * @param positions 107 * the positions to shift 108 * @return {@code round(uDecimal << positions)} 109 * @throws ArithmeticException 110 * if an overflow occurs and the arithmetic's 111 * {@link OverflowMode} is set to throw an exception 112 */ 113 public static final long shiftLeftChecked(DecimalArithmetic arith, DecimalRounding rounding, long uDecimal, int positions) { 114 if (positions >= 0) { 115 if (uDecimal == 0 | positions == 0) { 116 return uDecimal; 117 } 118 if (positions < Long.SIZE) { 119 if (uDecimal > 0) { 120 if (positions < Long.SIZE - 1) { 121 final int leadingZeros = Long.numberOfLeadingZeros(uDecimal); 122 if (leadingZeros > positions) { 123 return uDecimal << positions; 124 } 125 } 126 } else if (uDecimal > Long.MIN_VALUE) { 127 final int leadingZeros = Long.numberOfLeadingZeros(~uDecimal); 128 if (leadingZeros > positions) { 129 return uDecimal << positions; 130 } 131 } 132 } 133 throw new ArithmeticException("Overflow: " + arith.toString(uDecimal) + " << " + positions + " = " 134 + arith.toString(uDecimal << positions)); 135 } 136 // one shift missing for (-Integer.MIN_VALUE) but does not matter as 137 // result is always between 0 (incl) and 0.5 (excl) 138 return shiftRight(rounding, uDecimal, -positions > 0 ? -positions : Integer.MAX_VALUE); 139 } 140 141 /** 142 * Performs a shift right operation applying the given rounding mode if 143 * rounding is necessary. Throws an exception if an overflow occurs. 144 * 145 * @param arith 146 * the arithmetic associated with the shifted value 147 * @param rounding 148 * the rounding to apply if necessary 149 * @param uDecimal 150 * the value to shift 151 * @param positions 152 * the positions to shift 153 * @return {@code round(uDecimal >> positions)} 154 * @throws ArithmeticException 155 * if an overflow occurs and the arithmetic's 156 * {@link OverflowMode} is set to throw an exception 157 */ 158 public static final long shiftRightChecked(DecimalArithmetic arith, DecimalRounding rounding, long uDecimal, int positions) { 159 if (uDecimal == 0) { 160 return 0; 161 } 162 if (positions >= 0) { 163 return shiftRight(rounding, uDecimal, positions); 164 } 165 if (positions > -Long.SIZE) { 166 try { 167 return shiftLeftChecked(arith, rounding, uDecimal, -positions); 168 } catch (ArithmeticException e) { 169 // ignore, throw again below with correct shift direction 170 } 171 } 172 throw new ArithmeticException("Overflow: " + arith.toString(uDecimal) + " >> " + positions + " = " 173 + arith.toString(uDecimal >> positions)); 174 } 175 176 // no instances 177 private Shift() { 178 super(); 179 } 180}