001/* 002 * The MIT License (MIT) 003 * 004 * Copyright (c) 2015-2023 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.ScaleMetrics; 028import org.decimal4j.scale.Scales; 029import org.decimal4j.truncate.DecimalRounding; 030 031/** 032 * Contains static helper methods to round Decimal values. 033 */ 034final class Round { 035 036 /** 037 * Truncates the specified value to the given precision. 038 * 039 * @param arith 040 * the arithmetic associated with the value 041 * @param uDecimal 042 * the unscaled decimal value 043 * @param precision 044 * the precision to round to 045 * @return <code>round<sub>DOWN</sub>(uDecimal, precision)</code> 046 */ 047 public static final long round(DecimalArithmetic arith, long uDecimal, int precision) { 048 final ScaleMetrics scaleMetrics = arith.getScaleMetrics(); 049 final int scale = scaleMetrics.getScale(); 050 final ScaleMetrics deltaMetrics; 051 if (precision == 0) { 052 deltaMetrics = scaleMetrics; 053 } else if (precision < scale) { 054 final int deltaScale = scale - precision; 055 if (deltaScale <= 18) { 056 deltaMetrics = Scales.getScaleMetrics(scale - precision); 057 } else { 058 throw new IllegalArgumentException("scale - precision must be <= 18 but was " + deltaScale 059 + " for scale=" + scale + " and precision=" + precision); 060 } 061 } else { 062 // precision >= scale 063 return uDecimal; 064 } 065 return uDecimal - deltaMetrics.moduloByScaleFactor(uDecimal); 066 } 067 068 /** 069 * Rounds the specified value to the given precision. 070 * 071 * @param arith 072 * the arithmetic associated with the value 073 * @param rounding 074 * the rounding to apply 075 * @param uDecimal 076 * the unscaled decimal value 077 * @param precision 078 * the precision to round to 079 * @return <code>round(uDecimal, precision)</code> 080 */ 081 public static final long round(DecimalArithmetic arith, DecimalRounding rounding, long uDecimal, int precision) { 082 final ScaleMetrics scaleMetrics = arith.getScaleMetrics(); 083 final int scale = scaleMetrics.getScale(); 084 final int deltaScale = scale - precision; 085 final ScaleMetrics deltaMetrics; 086 if (precision == 0) { 087 deltaMetrics = scaleMetrics; 088 } else if (precision < scale) { 089 if (deltaScale <= 18) { 090 deltaMetrics = Scales.getScaleMetrics(scale - precision); 091 } else { 092 throw new IllegalArgumentException("scale - precision must be <= 18 but was " + deltaScale 093 + " for scale=" + scale + " and precision=" + precision); 094 } 095 } else { 096 // precision >= scale 097 return uDecimal; 098 } 099 if (uDecimal == 0) { 100 return 0; 101 } 102 final long truncatedDigits = deltaMetrics.moduloByScaleFactor(uDecimal); 103 final long truncatedValue = uDecimal - truncatedDigits; 104 final long truncatedOddEven = truncatedValue >> deltaScale; // move odd 105 // bit into 106 // place for 107 // HALF_EVEN 108 // rounding 109 final long roundingInc = Rounding.calculateRoundingIncrement(rounding, truncatedOddEven, truncatedDigits, 110 deltaMetrics.getScaleFactor()); 111 return arith.add(truncatedValue, roundingInc == 0 ? 0 : deltaMetrics.multiplyByScaleFactor(roundingInc));// must 112 // add 113 // via 114 // arith 115 // to 116 // check 117 // for 118 // overflow 119 } 120 121 // no instances 122 private Round() { 123 super(); 124 } 125 126}