1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.arrow.gandiva.evaluator; 19 20 import org.apache.arrow.vector.types.pojo.ArrowType.Decimal; 21 22 /** 23 * Utility methods for working with {@link Decimal} values. 24 */ 25 public class DecimalTypeUtil { DecimalTypeUtil()26 private DecimalTypeUtil() {} 27 28 /** 29 * Enum for supported mathematical operations. 30 */ 31 public enum OperationType { 32 ADD, 33 SUBTRACT, 34 MULTIPLY, 35 DIVIDE, 36 MOD 37 } 38 39 private static final int MIN_ADJUSTED_SCALE = 6; 40 /// The maximum precision representable by a 16-byte decimal 41 private static final int MAX_PRECISION = 38; 42 43 /** 44 * Determines the scale and precision of applying the given operation to the operands. 45 */ getResultTypeForOperation(OperationType operation, Decimal operand1, Decimal operand2)46 public static Decimal getResultTypeForOperation(OperationType operation, Decimal operand1, Decimal 47 operand2) { 48 int s1 = operand1.getScale(); 49 int s2 = operand2.getScale(); 50 int p1 = operand1.getPrecision(); 51 int p2 = operand2.getPrecision(); 52 int resultScale = 0; 53 int resultPrecision = 0; 54 switch (operation) { 55 case ADD: 56 case SUBTRACT: 57 resultScale = Math.max(operand1.getScale(), operand2.getScale()); 58 resultPrecision = resultScale + Math.max(operand1.getPrecision() - operand1.getScale(), 59 operand2.getPrecision() - operand2.getScale()) + 1; 60 break; 61 case MULTIPLY: 62 resultScale = s1 + s2; 63 resultPrecision = p1 + p2 + 1; 64 break; 65 case DIVIDE: 66 resultScale = 67 Math.max(MIN_ADJUSTED_SCALE, operand1.getScale() + operand2.getPrecision() + 1); 68 resultPrecision = 69 operand1.getPrecision() - operand1.getScale() + operand2.getScale() + resultScale; 70 break; 71 case MOD: 72 resultScale = Math.max(operand1.getScale(), operand2.getScale()); 73 resultPrecision = Math.min(operand1.getPrecision() - operand1.getScale(), 74 operand2.getPrecision() - operand2.getScale()) + 75 resultScale; 76 break; 77 default: 78 throw new RuntimeException("Needs support"); 79 } 80 return adjustScaleIfNeeded(resultPrecision, resultScale); 81 } 82 adjustScaleIfNeeded(int precision, int scale)83 private static Decimal adjustScaleIfNeeded(int precision, int scale) { 84 if (precision > MAX_PRECISION) { 85 int minScale = Math.min(scale, MIN_ADJUSTED_SCALE); 86 int delta = precision - MAX_PRECISION; 87 precision = MAX_PRECISION; 88 scale = Math.max(scale - delta, minScale); 89 } 90 return new Decimal(precision, scale); 91 } 92 93 } 94 95