All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.decimal4j.arithmetic.Shift Maven / Gradle / Ivy

Go to download

Java library for fast fixed precision arithmetics based on longs with support for up to 18 decimal places.

There is a newer version: 1.0.3
Show newest version
/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 decimal4j (tools4j), Marco Terzer
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.decimal4j.arithmetic;

import org.decimal4j.api.DecimalArithmetic;
import org.decimal4j.truncate.DecimalRounding;
import org.decimal4j.truncate.OverflowMode;
import org.decimal4j.truncate.TruncatedPart;

/**
 * Provides methods for left and right shifts.
 */
final class Shift {

	/**
	 * Performs a shift left operation applying the given rounding mode if
	 * rounding is necessary. Overflows are siltently truncated.
	 * 
	 * @param rounding
	 *            the rounding to apply for negative position (i.e. a right
	 *            shift)
	 * @param uDecimal
	 *            the value to shift
	 * @param positions
	 *            the positions to shift
	 * @return {@code round(uDecimal << positions)}
	 */
	public static final long shiftLeft(DecimalRounding rounding, long uDecimal, int positions) {
		if (positions >= 0) {
			return positions < Long.SIZE ? uDecimal << positions : 0;
		}
		// one shift missing for (-Integer.MIN_VALUE) but does not matter as
		// result is always between 0 (incl) and 0.5 (excl)
		return shiftRight(rounding, uDecimal, -positions > 0 ? -positions : Integer.MAX_VALUE);
	}

	/**
	 * Performs a shift right operation applying the given rounding mode if
	 * rounding is necessary. Overflows are siltently truncated.
	 * 
	 * @param rounding
	 *            the rounding to apply if necessary
	 * @param uDecimal
	 *            the value to shift
	 * @param positions
	 *            the positions to shift
	 * @return {@code round(uDecimal >> positions)}
	 */
	public static final long shiftRight(DecimalRounding rounding, long uDecimal, int positions) {
		if (uDecimal == 0 | positions == 0) {
			return uDecimal;
		}
		if (positions >= 0) {
			if (rounding == DecimalRounding.FLOOR) {
				return positions < Long.SIZE ? uDecimal >> positions : (uDecimal >= 0 ? 0 : -1);
			}
			if (positions < Long.SIZE) {
				final long truncated = uDecimal >= 0 ? (uDecimal >>> positions) : -(-uDecimal >>> positions);
				final long remainder = uDecimal - (truncated << positions);
				final TruncatedPart truncatedPart = positions == 63 ? Rounding.truncatedPartFor2pow63(remainder)
						: Rounding.truncatedPartFor(Math.abs(remainder), 1L << positions);
				return truncated + rounding.calculateRoundingIncrement(Long.signum(uDecimal), truncated, truncatedPart);
			}
			if (positions == Long.SIZE) {
				return rounding.calculateRoundingIncrement(Long.signum(uDecimal), 0,
						Rounding.truncatedPartFor2pow64(Math.abs(uDecimal)));
			}
			return rounding.calculateRoundingIncrement(Long.signum(uDecimal), 0,
					TruncatedPart.LESS_THAN_HALF_BUT_NOT_ZERO);
		}
		return positions > -Long.SIZE ? uDecimal << -positions : 0;
	}

	/**
	 * Performs a shift left operation applying the given rounding mode if
	 * rounding is necessary. Throws an exception if an overflow occurs.
	 * 
	 * @param arith
	 *            the arithmetic associated with the shifted value
	 * @param rounding
	 *            the rounding to apply for negative position (i.e. a right
	 *            shift)
	 * @param uDecimal
	 *            the value to shift
	 * @param positions
	 *            the positions to shift
	 * @return {@code round(uDecimal << positions)}
	 * @throws ArithmeticException
	 *             if an overflow occurs and the arithmetic's
	 *             {@link OverflowMode} is set to throw an exception
	 */
	public static final long shiftLeftChecked(DecimalArithmetic arith, DecimalRounding rounding, long uDecimal, int positions) {
		if (positions >= 0) {
			if (uDecimal == 0 | positions == 0) {
				return uDecimal;
			}
			if (positions < Long.SIZE) {
				if (uDecimal > 0) {
					if (positions < Long.SIZE - 1) {
						final int leadingZeros = Long.numberOfLeadingZeros(uDecimal);
						if (leadingZeros > positions) {
							return uDecimal << positions;
						}
					}
				} else if (uDecimal > Long.MIN_VALUE) {
					final int leadingZeros = Long.numberOfLeadingZeros(~uDecimal);
					if (leadingZeros > positions) {
						return uDecimal << positions;
					}
				}
			}
			throw new ArithmeticException("Overflow: " + arith.toString(uDecimal) + " << " + positions + " = "
					+ arith.toString(uDecimal << positions));
		}
		// one shift missing for (-Integer.MIN_VALUE) but does not matter as
		// result is always between 0 (incl) and 0.5 (excl)
		return shiftRight(rounding, uDecimal, -positions > 0 ? -positions : Integer.MAX_VALUE);
	}

	/**
	 * Performs a shift right operation applying the given rounding mode if
	 * rounding is necessary. Throws an exception if an overflow occurs.
	 * 
	 * @param arith
	 *            the arithmetic associated with the shifted value
	 * @param rounding
	 *            the rounding to apply if necessary
	 * @param uDecimal
	 *            the value to shift
	 * @param positions
	 *            the positions to shift
	 * @return {@code round(uDecimal >> positions)}
	 * @throws ArithmeticException
	 *             if an overflow occurs and the arithmetic's
	 *             {@link OverflowMode} is set to throw an exception
	 */
	public static final long shiftRightChecked(DecimalArithmetic arith, DecimalRounding rounding, long uDecimal, int positions) {
		if (uDecimal == 0) {
			return 0;
		}
		if (positions >= 0) {
			return shiftRight(rounding, uDecimal, positions);
		}
		if (positions > -Long.SIZE) {
			try {
				return shiftLeftChecked(arith, rounding, uDecimal, -positions);
			} catch (ArithmeticException e) {
				// ignore, throw again below with correct shift direction
			}
		}
		throw new ArithmeticException("Overflow: " + arith.toString(uDecimal) + " >> " + positions + " = "
				+ arith.toString(uDecimal >> positions));
	}

	// no instances
	private Shift() {
		super();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy