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; 028 029/** 030 * Provides static methods to calculate average of two numbers, that is, 031 * {@code (a+b)/2}. 032 */ 033final class Avg { 034 035 /** 036 * Calculates and returns the average of the two values rounded DOWN. 037 * 038 * @param a 039 * the first value 040 * @param b 041 * the second value 042 * @return <tt>round<sub>DOWN</sub>((a + b) / 2)</tt> 043 */ 044 public static final long avg(long a, long b) { 045 final long xor = a ^ b; 046 final long floor = (a & b) + (xor >> 1); 047 return floor + ((floor >>> 63) & xor); 048 } 049 050 /** 051 * Calculates and returns the average of the two values applying the given 052 * roundign if necessary. 053 * 054 * @param arith 055 * the arithmetic associated with the two values 056 * @param rounding 057 * the rounding to apply if necessary 058 * @param a 059 * the first value 060 * @param b 061 * the second value 062 * @return <tt>round((a + b) / 2)</tt> 063 */ 064 public static final long avg(DecimalArithmetic arith, DecimalRounding rounding, long a, long b) { 065 final long xor = a ^ b; 066 switch (rounding) { 067 case FLOOR: { 068 return (a & b) + (xor >> 1); 069 } 070 case CEILING: { 071 return (a | b) - (xor >> 1); 072 } 073 case DOWN:// fallthrough 074 case HALF_DOWN: { 075 final long floor = (a & b) + (xor >> 1); 076 return floor + ((floor >>> 63) & xor); 077 } 078 case UP:// fallthrough 079 case HALF_UP: { 080 final long floor = (a & b) + (xor >> 1); 081 return floor + ((~floor >>> 63) & xor); 082 } 083 case HALF_EVEN: { 084 final long xorShifted = xor >> 1; 085 final long floor = (a & b) + xorShifted; 086 // use ceiling if floor is odd 087 return ((floor & 0x1) == 0) ? floor : (a | b) - xorShifted; 088 } 089 case UNNECESSARY: { 090 final long floor = (a & b) + (xor >> 1); 091 if ((xor & 0x1) != 0) { 092 throw new ArithmeticException("Rounding necessary: " + arith.toString(a) + " avg " + arith.toString(b) 093 + " = " + arith.toString(floor)); 094 } 095 return floor; 096 } 097 default: { 098 // should not get here 099 throw new IllegalArgumentException("Unsupported rounding mode: " + rounding); 100 } 101 } 102 } 103 104 // no instances 105 private Avg() { 106 super(); 107 } 108}