/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hive.common.type; import java.util.Arrays; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.math.BigDecimal; import java.math.BigInteger; /** * HiveDecimal is a decimal data type with a maximum precision and scale. * <p> * It is the Hive DECIMAL data type. * <p> * The scale is the number of fractional decimal digits. The digits after the dot. It is limited * to 38 (MAX_SCALE). * <p> * The precision is the integer (or whole-number) decimal digits plus fractional decimal digits. * It is limited to a total of 38 digits (MAX_PRECISION). * <p> * Hive syntax for declaring DECIMAL has 3 forms: * <p> * {@code * DECIMAL // Use the default precision/scale.} * <p> * {@code * DECIMAL(precision) // Use the default scale.} * <p> * {@code * DECIMAL(precision, scale)} * } * <p> * The declared scale must be <= precision. * <p> * Use DECIMAL instead of DOUBLE when exact numeric accuracy is required. Not all decimal numbers * (radix 10) are exactly representable in the binary (radix 2 based) floating point type DOUBLE and * cause accuracy anomalies (i.e. wrong results). See the Internet for more details. * <p> * HiveDecimal is implemented as a classic Java immutable object. All operations on HiveDecimal * that produce a different value will create a new HiveDecimal object. * <p> * Decimals are physically stored without any extra leading or trailing zeroes. The scale of * a decimal is the number of non-trailing zero fractional digits. * <p> * Math operations on decimals typically cause the scale to change as a result of the math and * from trailing fractional digit elimination. * <p> * Typically, Hive, when it wants to make sure a result decimal fits in the column decimal's * precision/scale it calls enforcePrecisionScale. That method will scale down or trim off * result fractional digits if necessary with rounding when the column has a smaller scale. * And, it will also indicate overflow when the decimal has exceeded the column's maximum precision. * <p> * NOTE: When Hive gets ready to serialize a decimal into text or binary, it usually sometimes * wants trailing fractional zeroes. See the special notes for toFormatString and * bigIntegerBytesScaled for details. * <p> * ------------------------------------- Version 2 ------------------------------------------------ * <p> * This is the 2nd major version of HiveDecimal called V2. The previous version has been * renamed to HiveDecimalV1 and is kept as a test and behavior reference. * <p> * For good performance we do not represent the decimal using a BigDecimal object like the previous * version V1 did. Using Java objects to represent our decimal incurs too high a penalty * for memory allocations and general logic. * <p> * The original V1 public methods and fields are annotated with @HiveDecimalVersionV1; new public * methods and fields are annotated with @HiveDecimalVersionV2. * */ public final class HiveDecimal extends FastHiveDecimal implements Comparable<HiveDecimal> { /* * IMPLEMENTATION NOTE: * We implement HiveDecimal with the mutable FastHiveDecimal class. That class uses * protected on all its methods so they will not be visible in the HiveDecimal class. * * So even if one casts to FastHiveDecimal, you shouldn't be able to violate the immutability * of a HiveDecimal class. */ @HiveDecimalVersionV1 public static final int MAX_PRECISION = 38; @HiveDecimalVersionV1 public static final int MAX_SCALE = 38; /** * Default precision/scale when user doesn't specify in the column metadata, such as * decimal and decimal(8). */ @HiveDecimalVersionV1 public static final int USER_DEFAULT_PRECISION = 10; @HiveDecimalVersionV1 public static final int USER_DEFAULT_SCALE = 0; /** * Default precision/scale when system is not able to determine them, such as in case * of a non-generic udf. */ @HiveDecimalVersionV1 public static final int SYSTEM_DEFAULT_PRECISION = 38; @HiveDecimalVersionV1 public static final int SYSTEM_DEFAULT_SCALE = 18; /** * Common values. */ @HiveDecimalVersionV1 public static final HiveDecimal ZERO = HiveDecimal.create(0); @HiveDecimalVersionV1 public static final HiveDecimal ONE = HiveDecimal.create(1); /** * ROUND_FLOOR: * <p> * Round towards negative infinity. * <p> * The Hive function is FLOOR. * <p> * Positive numbers: The round fraction is thrown away. * <p> * (Example here rounds at scale 0) * Value FLOOR * 0.3 0 * 2 2 * 2.1 2 * <p> * Negative numbers: If there is a round fraction, throw it away and subtract 1. * <p> * (Example here rounds at scale 0) * Value FLOOR * -0.3 -1 * -2 -2 * -2.1 -3 */ @HiveDecimalVersionV1 public static final int ROUND_FLOOR = BigDecimal.ROUND_FLOOR; /** * ROUND_CEILING: * <p> * Round towards positive infinity. * <p> * The Hive function is CEILING. * <p> * Positive numbers: If there is a round fraction, throw it away and add 1 * <p> * (Example here rounds at scale 0) * Value CEILING * 0.3 1 * 2 2 * 2.1 3 * <p> * Negative numbers: The round fraction is thrown away. * <p> * (Example here rounds at scale 0) * Value CEILING * -0.3 0 * -2 -2 * -2.1 -2 */ @HiveDecimalVersionV1 public static final int ROUND_CEILING = BigDecimal.ROUND_CEILING; /** * ROUND_HALF_UP: * <p> * Round towards "nearest neighbor" unless both neighbors are equidistant then round up. * <p> * The Hive function is ROUND. * <p> * For result, throw away round fraction. If the round fraction is >= 0.5, then add 1 when * positive and subtract 1 when negative. So, the sign is irrelevant. * <p> * (Example here rounds at scale 0) * Value ROUND Value ROUND * 0.3 0 -0.3 0 * 2 2 -2 -2 * 2.1 2 -2.1 -2 * 2.49 2 -2.49 -2 * 2.5 3 -2.5 -3 * */ @HiveDecimalVersionV1 public static final int ROUND_HALF_UP = BigDecimal.ROUND_HALF_UP; /** * ROUND_HALF_EVEN: * Round towards the "nearest neighbor" unless both neighbors are equidistant, then round * towards the even neighbor. * <p> * The Hive function is BROUND. * <p> * Known as Banker’s Rounding. * <p> * When you add values rounded with ROUND_HALF_UP you have a bias that grows as you add more * numbers. Banker's Rounding is a way to minimize that bias. It rounds toward the nearest * even number when the fraction is 0.5 exactly. In table below, notice that 2.5 goes DOWN to * 2 (even) but 3.5 goes UP to 4 (even), etc. * <p> * So, the sign is irrelevant. * <p> * (Example here rounds at scale 0) * Value BROUND Value BROUND * 0.49 0 -0.49 0 * 0.5 0 -0.5 0 * 0.51 1 -0.51 -1 * 1.5 2 -1.5 -2 * 2.5 2 -2.5 -2 * 2.51 3 -2.51 -3 * 3.5 4 -3.5 -4 * 4.5 4 -4.5 -4 * 4.51 5 -4.51 -5 * */ @HiveDecimalVersionV1 public static final int ROUND_HALF_EVEN = BigDecimal.ROUND_HALF_EVEN; //----------------------------------------------------------------------------------------------- // Constructors are marked private; use create methods. //----------------------------------------------------------------------------------------------- private HiveDecimal() { super(); } private HiveDecimal(HiveDecimal dec) { super(dec); } private HiveDecimal(FastHiveDecimal fastDec) { super(fastDec); } private HiveDecimal(int fastSignum, FastHiveDecimal fastDec) { super(fastSignum, fastDec); } private HiveDecimal( int fastSignum, long fast0, long fast1, long fast2, int fastIntegerDigitCount, int fastScale) { super(fastSignum, fast0, fast1, fast2, fastIntegerDigitCount, fastScale); } //----------------------------------------------------------------------------------------------- // Create methods. //----------------------------------------------------------------------------------------------- /** * Create a HiveDecimal from a FastHiveDecimal object. Used by HiveDecimalWritable. * @param fastDec the value to set * @return new hive decimal */ @HiveDecimalVersionV2 public static HiveDecimal createFromFast(FastHiveDecimal fastDec) { return new HiveDecimal(fastDec); } /** * Create a HiveDecimal from BigDecimal object. * * A BigDecimal object has a decimal scale. * * We will have overflow if BigDecimal's integer part exceed MAX_PRECISION digits or * 99,999,999,999,999,999,999,999,999,999,999,999,999 or 10^38 - 1. * * When the BigDecimal value's precision exceeds MAX_PRECISION and there are fractional digits * because of scale > 0, then lower digits are trimmed off with rounding to meet the * MAX_PRECISION requirement. * * Also, BigDecimal supports negative scale -- which means multiplying the value by 10^abs(scale). * And, BigDecimal allows for a non-zero scale for zero. We normalize that so zero always has * scale 0. * * @param bigDecimal the value to set * @return The HiveDecimal with the BigDecimal's value adjusted down to a maximum precision. * Otherwise, null is returned for overflow. */ @HiveDecimalVersionV1 public static HiveDecimal create(BigDecimal bigDecimal) { return create(bigDecimal, true); } /** * Same as the above create method, except fractional digit rounding can be turned off. * @param bigDecimal the value to set * @param allowRounding True requires all of the bigDecimal value be converted to the decimal * without loss of precision. * @return */ @HiveDecimalVersionV1 public static HiveDecimal create(BigDecimal bigDecimal, boolean allowRounding) { HiveDecimal result = new HiveDecimal(); if (!result.fastSetFromBigDecimal( bigDecimal, allowRounding)) { return null; } return result; } /** * Creates a HiveDecimal from a BigInteger's value with a scale of 0. * * We will have overflow if BigInteger exceed MAX_PRECISION digits or * 99,999,999,999,999,999,999,999,999,999,999,999,999 or 10^38 - 1. * * @param bigInteger the value to set * @return A HiveDecimal object with the exact BigInteger's value. * Otherwise, null is returned on overflow. */ @HiveDecimalVersionV1 public static HiveDecimal create(BigInteger bigInteger) { HiveDecimal result = new HiveDecimal(); if (!result.fastSetFromBigInteger( bigInteger)) { return null; } return result; } /** * Creates a HiveDecimal from a BigInteger's value with a specified scale. * * We will have overflow if BigInteger exceed MAX_PRECISION digits or * 99,999,999,999,999,999,999,999,999,999,999,999,999 or 10^38 - 1. * * The resulting decimal will have fractional digits when the specified scale is greater than 0. * * When the BigInteger's value's precision exceeds MAX_PRECISION and there are fractional digits * because of scale > 0, then lower digits are trimmed off with rounding to meet the * MAX_PRECISION requirement. * * @param bigInteger the value to set * @param scale the scale to set * @return A HiveDecimal object with the BigInteger's value adjusted for scale. * Otherwise, null is returned on overflow. */ @HiveDecimalVersionV1 public static HiveDecimal create(BigInteger bigInteger, int scale) { HiveDecimal result = new HiveDecimal(); if (!result.fastSetFromBigIntegerAndScale( bigInteger, scale)) { return null; } return result; } /** * Create a HiveDecimal by parsing a whole string. * * We support parsing a decimal with an exponent because the previous version * (i.e. OldHiveDecimal) uses the BigDecimal parser and was able to. * * @param string the string to parse * @return a new hive decimal */ @HiveDecimalVersionV1 public static HiveDecimal create(String string) { HiveDecimal result = new HiveDecimal(); if (!result.fastSetFromString( string, true)) { return null; } return result; } /** * Same as the method above, except blanks before and after are tolerated. * @param string the string to parse * @param trimBlanks True specifies leading and trailing blanks are to be ignored. * @return a new hive decimal */ @HiveDecimalVersionV2 public static HiveDecimal create(String string, boolean trimBlanks) { HiveDecimal result = new HiveDecimal(); if (!result.fastSetFromString( string, trimBlanks)) { return null; } return result; } /** * Create a HiveDecimal by parsing the characters in a whole byte array. * * Same rules as create(String string) above. * */ @HiveDecimalVersionV2 public static HiveDecimal create(byte[] bytes) { HiveDecimal result = new HiveDecimal(); if (!result.fastSetFromBytes( bytes, 0, bytes.length, false)) { return null; } return result; } /** * Same as the method above, except blanks before and after are tolerated. * */ @HiveDecimalVersionV2 public static HiveDecimal create(byte[] bytes, boolean trimBlanks) { HiveDecimal result = new HiveDecimal(); if (!result.fastSetFromBytes( bytes, 0, bytes.length, trimBlanks)) { return null; } return result; } /** * This method takes in digits only UTF-8 characters, a sign flag, and a scale and returns * a decimal. */ @HiveDecimalVersionV2 public static HiveDecimal create(boolean isNegative, byte[] bytes, int scale) { HiveDecimal result = new HiveDecimal(); if (!result.fastSetFromDigitsOnlyBytesAndScale( isNegative, bytes, 0, bytes.length, scale)) { return null; } if (isNegative) { result.fastNegate(); } return result; } @HiveDecimalVersionV2 public static HiveDecimal create( boolean isNegative, byte[] bytes, int offset, int length, int scale) { HiveDecimal result = new HiveDecimal(); if (!result.fastSetFromDigitsOnlyBytesAndScale( isNegative, bytes, offset, length, scale)) { return null; } return result; } /** * Create a HiveDecimal by parsing the characters in a slice of a byte array. * * Same rules as create(String string) above. * */ @HiveDecimalVersionV2 public static HiveDecimal create(byte[] bytes, int offset, int length) { HiveDecimal result = new HiveDecimal(); if (!result.fastSetFromBytes( bytes, offset, length, false)) { return null; } return result; } /** * Same as the method above, except blanks before and after are tolerated. * */ @HiveDecimalVersionV2 public static HiveDecimal create( byte[] bytes, int offset, int length, boolean trimBlanks) { HiveDecimal result = new HiveDecimal(); if (!result.fastSetFromBytes( bytes, offset, length, trimBlanks)) { return null; } return result; } /** * Create a HiveDecimal object from an int. * */ @HiveDecimalVersionV1 public static HiveDecimal create(int intValue) { HiveDecimal result = new HiveDecimal(); result.fastSetFromInt(intValue); return result; } /** * Create a HiveDecimal object from a long. * */ @HiveDecimalVersionV1 public static HiveDecimal create(long longValue) { HiveDecimal result = new HiveDecimal(); result.fastSetFromLong(longValue); return result; } /** * Create a HiveDecimal object from a long with a specified scale. * */ @HiveDecimalVersionV2 public static HiveDecimal create(long longValue, int scale) { HiveDecimal result = new HiveDecimal(); if (!result.fastSetFromLongAndScale( longValue, scale)) { return null; } return result; } /** * Create a HiveDecimal object from a float. * <p> * This method is equivalent to HiveDecimal.create(Float.toString(floatValue)) */ @HiveDecimalVersionV2 public static HiveDecimal create(float floatValue) { HiveDecimal result = new HiveDecimal(); if (!result.fastSetFromFloat(floatValue)) { return null; } return result; } /** * Create a HiveDecimal object from a double. * <p> * This method is equivalent to HiveDecimal.create(Double.toString(doubleValue)) */ @HiveDecimalVersionV2 public static HiveDecimal create(double doubleValue) { HiveDecimal result = new HiveDecimal(); if (!result.fastSetFromDouble(doubleValue)) { return null; } return result; } //----------------------------------------------------------------------------------------------- // Serialization methods. //----------------------------------------------------------------------------------------------- // The byte length of the scratch byte array that needs to be passed to serializationUtilsRead. @HiveDecimalVersionV2 public static final int SCRATCH_BUFFER_LEN_SERIALIZATION_UTILS_READ = FAST_SCRATCH_BUFFER_LEN_SERIALIZATION_UTILS_READ; /** * Deserialize data written in the format used by the SerializationUtils methods * readBigInteger/writeBigInteger and create a decimal using the supplied scale. * <p> * ORC uses those SerializationUtils methods for its serialization. * <p> * A scratch bytes array is necessary to do the binary to decimal conversion for better * performance. Pass a SCRATCH_BUFFER_LEN_SERIALIZATION_UTILS_READ byte array for scratchBytes. * <p> * @return The deserialized decimal or null if the conversion failed. */ @HiveDecimalVersionV2 public static HiveDecimal serializationUtilsRead( InputStream inputStream, int scale, byte[] scratchBytes) throws IOException { HiveDecimal result = new HiveDecimal(); if (!result.fastSerializationUtilsRead( inputStream, scale, scratchBytes)) { return null; } return result; } /** * Convert bytes in the format used by BigInteger's toByteArray format (and accepted by its * constructor) into a decimal using the specified scale. * <p> * Our bigIntegerBytes methods create bytes in this format, too. * <p> * This method is designed for high performance and does not create an actual BigInteger during * binary to decimal conversion. * */ @HiveDecimalVersionV2 public static HiveDecimal createFromBigIntegerBytesAndScale( byte[] bytes, int scale) { HiveDecimal result = new HiveDecimal(); if (!result.fastSetFromBigIntegerBytesAndScale( bytes, 0, bytes.length, scale)) { return null; } return result; } @HiveDecimalVersionV2 public static HiveDecimal createFromBigIntegerBytesAndScale( byte[] bytes, int offset, int length, int scale) { HiveDecimal result = new HiveDecimal(); if (!result.fastSetFromBigIntegerBytesAndScale( bytes, offset, length, scale)) { return null; } return result; } // The length of the long array that needs to be passed to serializationUtilsWrite. @HiveDecimalVersionV2 public static final int SCRATCH_LONGS_LEN = FAST_SCRATCH_LONGS_LEN; /** * Serialize this decimal's BigInteger equivalent unscaled value using the format that the * SerializationUtils methods readBigInteger/writeBigInteger use. * <p> * ORC uses those SerializationUtils methods for its serialization. * <p> * Scratch objects necessary to do the decimal to binary conversion without actually creating a * BigInteger object are passed for better performance. * <p> * Allocate scratchLongs with SCRATCH_LONGS_LEN longs. * */ @HiveDecimalVersionV2 public boolean serializationUtilsWrite( OutputStream outputStream, long[] scratchLongs) throws IOException { return fastSerializationUtilsWrite( outputStream, scratchLongs); } // The length of the scratch byte array that needs to be passed to bigIntegerBytes, etc. @HiveDecimalVersionV2 public static final int SCRATCH_BUFFER_LEN_BIG_INTEGER_BYTES = FAST_SCRATCH_BUFFER_LEN_BIG_INTEGER_BYTES; /** * Return binary representation of this decimal's BigInteger equivalent unscaled value using * the format that the BigInteger's toByteArray method returns (and the BigInteger constructor * accepts). * <p> * Used by LazyBinary, Avro, and Parquet serialization. * <p> * Scratch objects necessary to do the decimal to binary conversion without actually creating a * BigInteger object are passed for better performance. * <p> * Allocate scratchLongs with SCRATCH_LONGS_LEN longs. * And, allocate buffer with SCRATCH_BUFFER_LEN_BIG_INTEGER_BYTES bytes. * <p> * @param scratchLongs * @param buffer * @return The number of bytes used for the binary result in buffer. Otherwise, 0 if the * conversion failed. */ @HiveDecimalVersionV2 public int bigIntegerBytes( long[] scratchLongs, byte[] buffer) { return fastBigIntegerBytes( scratchLongs, buffer); } @HiveDecimalVersionV2 public byte[] bigIntegerBytes() { long[] scratchLongs = new long[SCRATCH_LONGS_LEN]; byte[] buffer = new byte[SCRATCH_BUFFER_LEN_BIG_INTEGER_BYTES]; final int byteLength = fastBigIntegerBytes( scratchLongs, buffer); return Arrays.copyOfRange(buffer, 0, byteLength); } /** * Convert decimal to BigInteger binary bytes with a serialize scale, similar to the formatScale * for toFormatString. It adds trailing zeroes the (emulated) BigInteger toByteArray result * when a serializeScale is greater than current scale. Or, rounds if scale is less than * current scale. * <p> * Used by Avro and Parquet serialization. * <p> * This emulates the OldHiveDecimal setScale AND THEN OldHiveDecimal getInternalStorage() behavior. * */ @HiveDecimalVersionV2 public int bigIntegerBytesScaled( int serializeScale, long[] scratchLongs, byte[] buffer) { return fastBigIntegerBytesScaled( serializeScale, scratchLongs, buffer); } @HiveDecimalVersionV2 public byte[] bigIntegerBytesScaled(int serializeScale) { long[] scratchLongs = new long[SCRATCH_LONGS_LEN]; byte[] buffer = new byte[SCRATCH_BUFFER_LEN_BIG_INTEGER_BYTES]; int byteLength = fastBigIntegerBytesScaled( serializeScale, scratchLongs, buffer); return Arrays.copyOfRange(buffer, 0, byteLength); } //----------------------------------------------------------------------------------------------- // Convert to string/UTF-8 ASCII bytes methods. //----------------------------------------------------------------------------------------------- /** * Return a string representation of the decimal. * <p> * It is the equivalent of calling bigDecimalValue().toPlainString -- it does not add exponent * notation -- but is much faster. * <p> * NOTE: If setScale(int serializationScale) was used to create the decimal object, then trailing * fractional digits will be added to display to the serializationScale. Or, the display may * get rounded. See the comments for that method. * */ @HiveDecimalVersionV1 @Override public String toString() { if (fastSerializationScale() != -1) { // Use the serialization scale and format the string with trailing zeroes (or // round the decimal) if necessary. return fastToFormatString(fastSerializationScale()); } else { return fastToString(); } } @HiveDecimalVersionV2 public String toString( byte[] scratchBuffer) { if (fastSerializationScale() != -1) { // Use the serialization scale and format the string with trailing zeroes (or // round the decimal) if necessary. return fastToFormatString( fastSerializationScale(), scratchBuffer); } else { return fastToString(scratchBuffer); } } /** * Return a string representation of the decimal using the specified scale. * <p> * This method is designed to ALWAYS SUCCEED (unless the newScale parameter is out of range). * <p> * Is does the equivalent of a setScale(int newScale). So, more than 38 digits may be returned. * See that method for more details on how this can happen. * <p> * @param formatScale The number of digits after the decimal point * @return The scaled decimal representation string representation. */ @HiveDecimalVersionV1 public String toFormatString(int formatScale) { return fastToFormatString( formatScale); } @HiveDecimalVersionV2 public String toFormatString(int formatScale, byte[] scratchBuffer) { return fastToFormatString( formatScale, scratchBuffer); } @HiveDecimalVersionV2 public String toDigitsOnlyString() { return fastToDigitsOnlyString(); } // The length of the scratch buffer that needs to be passed to toBytes, toFormatBytes, // toDigitsOnlyBytes. @HiveDecimalVersionV2 public final static int SCRATCH_BUFFER_LEN_TO_BYTES = FAST_SCRATCH_BUFFER_LEN_TO_BYTES; /** * Decimal to ASCII bytes conversion. * <p> * The scratch buffer will contain the result afterwards. It should be * SCRATCH_BUFFER_LEN_TO_BYTES bytes long. * <p> * The result is produced at the end of the scratch buffer, so the return value is the byte * index of the first byte. The byte slice is [byteIndex:SCRATCH_BUFFER_LEN_TO_BYTES-1]. * */ @HiveDecimalVersionV2 public int toBytes( byte[] scratchBuffer) { return fastToBytes( scratchBuffer); } /** * This is the serialization version of decimal to string conversion. * <p> * It adds trailing zeroes when the formatScale is greater than the current scale. Or, it * does round if the formatScale is less than the current scale. * <p> * Note that you can get more than 38 (MAX_PRECISION) digits in the output with this method. * */ @HiveDecimalVersionV2 public int toFormatBytes( int formatScale, byte[] scratchBuffer) { return fastToFormatBytes( formatScale, scratchBuffer); } /** * Convert decimal to just the digits -- no dot. * <p> * Currently used by BinarySortable serialization. * <p> * A faster way to get just the digits than calling unscaledValue.toString().getBytes(). * */ @HiveDecimalVersionV2 public int toDigitsOnlyBytes( byte[] scratchBuffer) { return fastToDigitsOnlyBytes( scratchBuffer); } //----------------------------------------------------------------------------------------------- // Comparison methods. //----------------------------------------------------------------------------------------------- @HiveDecimalVersionV1 @Override public int compareTo(HiveDecimal dec) { return fastCompareTo(dec); } /** * Hash code based on (new) decimal representation. * <p> * Faster than hashCode(). * <p> * Used by map join and other Hive internal purposes where performance is important. * <p> * IMPORTANT: See comments for hashCode(), too. */ @HiveDecimalVersionV2 public int newFasterHashCode() { return fastNewFasterHashCode(); } /** * This is returns original hash code as returned by HiveDecimalV1. * <p> * We need this when the HiveDecimalV1 hash code has been exposed and and written or affected * how data is written. * <p> * This method supports compatibility. * <p> * Examples: bucketing, Hive hash() function, and Hive statistics. * <p> * NOTE: It is necessary to create a BigDecimal object and use its hash code, so this method is * slow. */ @HiveDecimalVersionV1 @Override public int hashCode() { return fastHashCode(); } /** * Are two decimal content (values) equal? * <p> * @param obj The 2nd decimal. * @return When obj is null or not class HiveDecimal, the return is false. * Otherwise, returns true when the decimal values are exactly equal. */ @HiveDecimalVersionV1 @Override public boolean equals(Object obj) { if (obj == null || obj.getClass() != getClass()) { return false; } return fastEquals((HiveDecimal) obj); } //----------------------------------------------------------------------------------------------- // Attribute methods. //----------------------------------------------------------------------------------------------- /** * Returns the scale of the decimal. Range 0 .. MAX_SCALE. * */ @HiveDecimalVersionV1 public int scale() { return fastScale(); } /** * Returns the number of integer digits in the decimal. * <p> * When the integer portion is zero, this method returns 0. * */ @HiveDecimalVersionV2 public int integerDigitCount() { return fastIntegerDigitCount(); } /** * Returns the number of digits (integer and fractional) in the number, which is equivalent * to SQL decimal precision. * <p> * Note that this method is different from rawPrecision(), which returns the number of digits * ignoring the scale. Note that rawPrecision returns 0 when the value is 0. * * Decimal precision rawPrecision * 0 1 0 * 1 1 1 * -7 1 1 * 0.1 1 1 * 0.04 2 1 * 0.00380 5 3 * 104.0009 7 7 * <p> * If you just want the actual number of digits, use rawPrecision(). * */ @HiveDecimalVersionV1 public int precision() { return fastSqlPrecision(); } // See comments for sqlPrecision. @HiveDecimalVersionV2 public int rawPrecision() { return fastRawPrecision(); } /** * Get the sign of the decimal. * <p> * @return 0 if the decimal is equal to 0, -1 if less than zero, and 1 if greater than 0 */ @HiveDecimalVersionV1 public int signum() { return fastSignum(); } //----------------------------------------------------------------------------------------------- // Value conversion methods. //----------------------------------------------------------------------------------------------- /** * Is the decimal value a byte? Range -128 to 127. * Byte.MIN_VALUE Byte.MAX_VALUE * <p> * Emulates testing for no value corruption: * bigDecimalValue().setScale(0).equals(BigDecimal.valueOf(bigDecimalValue().byteValue())) * <p> * NOTE: Fractional digits are ignored in the test since byteValue() will * remove them (round down). * <p> * @return True when byteValue() will return a correct byte. */ @HiveDecimalVersionV2 public boolean isByte() { return fastIsByte(); } /** * A byte variation of longValue() * <p> * This method will return a corrupted value unless isByte() is true. */ @HiveDecimalVersionV1 public byte byteValue() { return fastByteValueClip(); } /** * Is the decimal value a short? Range -32,768 to 32,767. * Short.MIN_VALUE Short.MAX_VALUE * <p> * Emulates testing for no value corruption: * bigDecimalValue().setScale(0).equals(BigDecimal.valueOf(bigDecimalValue().shortValue())) * <p> * NOTE: Fractional digits are ignored in the test since shortValue() will * remove them (round down). * <p> * @return True when shortValue() will return a correct short. */ @HiveDecimalVersionV2 public boolean isShort() { return fastIsShort(); } /** * A short variation of longValue(). * <p> * This method will return a corrupted value unless isShort() is true. */ @HiveDecimalVersionV1 public short shortValue() { return fastShortValueClip(); } /** * Is the decimal value a int? Range -2,147,483,648 to 2,147,483,647. * Integer.MIN_VALUE Integer.MAX_VALUE * <p> * Emulates testing for no value corruption: * bigDecimalValue().setScale(0).equals(BigDecimal.valueOf(bigDecimalValue().intValue())) * <p> * NOTE: Fractional digits are ignored in the test since intValue() will * remove them (round down). * <p> * @return True when intValue() will return a correct int. */ @HiveDecimalVersionV2 public boolean isInt() { return fastIsInt(); } /** * An int variation of longValue(). * <p> * This method will return a corrupted value unless isInt() is true. */ @HiveDecimalVersionV1 public int intValue() { return fastIntValueClip(); } /** * Is the decimal value a long? Range -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807. * Long.MIN_VALUE Long.MAX_VALUE * <p> * Emulates testing for no value corruption: * bigDecimalValue().setScale(0).equals(BigDecimal.valueOf(bigDecimalValue().longValue())) * <p> * NOTE: Fractional digits are ignored in the test since longValue() will * remove them (round down). * <p> * @return True when longValue() will return a correct long. */ @HiveDecimalVersionV2 public boolean isLong() { return fastIsLong(); } /** * Return the long value of a decimal. * <p> * This method will return a corrupted value unless isLong() is true. */ @HiveDecimalVersionV1 public long longValue() { return fastLongValueClip(); } @HiveDecimalVersionV1 public long longValueExact() { if (!isLong()) { throw new ArithmeticException(); } return fastLongValueClip(); } /** * Return a float representing the decimal. Due the limitations of float, some values will not * be accurate. * */ @HiveDecimalVersionV1 public float floatValue() { return fastFloatValue(); } /** * Return a double representing the decimal. Due the limitations of double, some values will not * be accurate. * */ @HiveDecimalVersionV1 public double doubleValue() { return fastDoubleValue(); } /** * Return a BigDecimal representing the decimal. The BigDecimal class is able to accurately * represent the decimal. * * NOTE: We are not representing our decimal as BigDecimal now as OldHiveDecimal did, so this * is now slower. * */ @HiveDecimalVersionV1 public BigDecimal bigDecimalValue() { return fastBigDecimalValue(); } /** * Get a BigInteger representing the decimal's digits without a dot. * <p> * @return Returns a signed BigInteger. */ @HiveDecimalVersionV1 public BigInteger unscaledValue() { return fastBigIntegerValue(); } /** * Return a decimal with only the fractional digits. * <p> * Zero is returned when there are no fractional digits (i.e. scale is 0). * */ @HiveDecimalVersionV2 public HiveDecimal fractionPortion() { HiveDecimal result = new HiveDecimal(); result.fastFractionPortion(); return result; } /** * Return a decimal with only the integer digits. * <p> * Any fractional digits are removed. E.g. 2.083 scale 3 returns as 2 scale 0. * */ @HiveDecimalVersionV2 public HiveDecimal integerPortion() { HiveDecimal result = new HiveDecimal(); result.fastIntegerPortion(); return result; } //----------------------------------------------------------------------------------------------- // Math methods. //----------------------------------------------------------------------------------------------- /** * Add the current decimal and another decimal and return the result. * */ @HiveDecimalVersionV1 public HiveDecimal add(HiveDecimal dec) { HiveDecimal result = new HiveDecimal(); if (!fastAdd( dec, result)) { return null; } return result; } /** * Subtract from the current decimal another decimal and return the result. * */ @HiveDecimalVersionV1 public HiveDecimal subtract(HiveDecimal dec) { HiveDecimal result = new HiveDecimal(); if (!fastSubtract( dec, result)) { return null; } return result; } /** * Multiply two decimals. * <p> * NOTE: Overflow Determination for Multiply * <p> * OldDecimal.multiply performs the multiply with BigDecimal but DOES NOT ALLOW ROUNDING * (i.e. no throwing away lower fractional digits). * <p> * CONSIDER: Allowing rounding. This would eliminate cases today where we return null for * the multiplication result. * <p> * IMPLEMENTATION NOTE: HiveDecimalV1 code does this: * <p> * return create(bd.multiply(dec.bd), false); */ @HiveDecimalVersionV1 public HiveDecimal multiply(HiveDecimal dec) { HiveDecimal result = new HiveDecimal(); if (!fastMultiply( dec, result)) { return null; } return result; } /** * Multiplies a decimal by a power of 10. * <p> * The decimal 19350 scale 0 will return 193.5 scale 1 when power is -2 (negative). * <p> * The decimal 1.000923 scale 6 will return 10009.23 scale 2 when power is 4 (positive). * <p> * @param power * @return Returns a HiveDecimal whose value is value * 10^power. */ @HiveDecimalVersionV1 public HiveDecimal scaleByPowerOfTen(int power) { if (power == 0 || fastSignum() == 0) { // No change for multiply by 10^0 or value 0. return this; } HiveDecimal result = new HiveDecimal(); if (!fastScaleByPowerOfTen( power, result)) { return null; } return result; } /** * Take the absolute value of a decimal. * <p> * @return When the decimal is negative, returns a new HiveDecimal with the positive value. * Otherwise, returns the current 0 or positive value object; */ @HiveDecimalVersionV1 public HiveDecimal abs() { if (fastSignum() != -1) { return this; } HiveDecimal result = new HiveDecimal(this); result.fastAbs(); return result; } /** * Reverse the sign of a decimal. * <p> * @return Returns a new decimal with the sign flipped. When the value is 0, the current * object is returned. */ @HiveDecimalVersionV1 public HiveDecimal negate() { if (fastSignum() == 0) { return this; } HiveDecimal result = new HiveDecimal(this); result.fastNegate(); return result; } //----------------------------------------------------------------------------------------------- // Rounding / setScale methods. //----------------------------------------------------------------------------------------------- /** * DEPRECATED for V2. * <p> * Create a decimal from another decimal whose only change is it is MARKED and will display / * serialize with a specified scale that will add trailing zeroes (or round) if necessary. * <p> * After display / serialization, the MARKED object is typically thrown away. * <p> * A MARKED decimal ONLY affects these 2 methods since these were the only ways setScale was * used in the old code. * <p> * toString * unscaleValue * <p> * This method has been deprecated because has poor performance by creating a throw away object. * <p> * For setScale(scale).toString() use toFormatString(scale) instead. * For setScale(scale).unscaledValue().toByteArray() use V2 bigIntegerBytesScaled(scale) instead. * <p> * For better performance, use the V2 form of toFormatString that takes a scratch buffer, * or even better use toFormatBytes. * <p> * And, use the form of bigIntegerBytesScaled that takes scratch objects for better performance. * */ @Deprecated @HiveDecimalVersionV1 public HiveDecimal setScale(int serializationScale) { HiveDecimal result = new HiveDecimal(this); result.fastSetSerializationScale(serializationScale); return result; } /** * Do decimal rounding and return the result. * <p> * When the roundingPoint is 0 or positive, we round away lower fractional digits if the * roundingPoint is less than current scale. In this case, we will round the result using the * specified rounding mode. * <p> * When the roundingPoint is negative, the rounding will occur within the integer digits. Integer * digits below the roundPoint will be cleared. If the rounding occurred, a one will be added * just above the roundingPoint. Note this may cause overflow. * <p> * No effect when the roundingPoint equals the current scale. The current object is returned. * <p> * The name setScale is taken from BigDecimal.setScale -- a better name would have been round. * */ @HiveDecimalVersionV1 public HiveDecimal setScale( int roundingPoint, int roundingMode) { if (fastScale() == roundingPoint) { // No change. return this; } // Even if we are just setting the scale when newScale is greater than the current scale, // we need a new object to obey our immutable behavior. HiveDecimal result = new HiveDecimal(); if (!fastRound( roundingPoint, roundingMode, result)) { return null; } return result; } /** * Return the result of decimal^exponent * <p> * CONSIDER: Currently, negative exponent is not supported. * CONSIDER: Does anybody use this method? * */ @HiveDecimalVersionV1 public HiveDecimal pow(int exponent) { HiveDecimal result = new HiveDecimal(this); if (!fastPow( exponent, result)) { return null; } return result; } /** * Divides this decimal by another decimal and returns a new decimal with the result. * */ @HiveDecimalVersionV1 public HiveDecimal divide(HiveDecimal divisor) { HiveDecimal result = new HiveDecimal(); if (!fastDivide( divisor, result)) { return null; } return result; } /** * Divides this decimal by another decimal and returns a new decimal with the remainder of the * division. * <p> * value is (decimal % divisor) * <p> * The remainder is equivalent to BigDecimal: * bigDecimalValue().subtract(bigDecimalValue().divideToIntegralValue(divisor).multiply(divisor)) * */ @HiveDecimalVersionV1 public HiveDecimal remainder(HiveDecimal divisor) { HiveDecimal result = new HiveDecimal(); if (!fastRemainder( divisor, result)) { return null; } return result; } //----------------------------------------------------------------------------------------------- // Precision/scale enforcement methods. //----------------------------------------------------------------------------------------------- /** * Determine if a decimal fits within a specified maxPrecision and maxScale, and round * off fractional digits if necessary to make the decimal fit. * <p> * The relationship between the enforcement maxPrecision and maxScale is restricted. The * specified maxScale must be less than or equal to the maxPrecision. * <p> * Normally, decimals that result from creation operation, arithmetic operations, etc are * "free range" up to MAX_PRECISION and MAX_SCALE. Each operation checks if the result decimal * is beyond MAX_PRECISION and MAX_SCALE. If so the result decimal is rounded off using * ROUND_HALF_UP. If the round digit is 5 or more, one is added to the lowest remaining digit. * The round digit is the digit just below the round point. Result overflow can occur if a * result decimal's integer portion exceeds MAX_PRECISION. * <p> * This method supports enforcing to a declared Hive DECIMAL's precision/scale. * E.g. DECIMAL(10,4) * <p> * Here are the enforcement/rounding checks of this method: * <p> * 1) Maximum integer digits = maxPrecision - maxScale * <p> * If the decimal's integer digit count exceeds this, the decimal does not fit (overflow). * <p> * 2) If decimal's scale is greater than maxScale, then excess fractional digits are * rounded off. When rounding increases the remaining decimal, it may exceed the * limits and overflow. * <p> * @param dec * @param maxPrecision * @param maxScale * @return The original decimal if no adjustment is necessary. * A rounded off decimal if adjustment was necessary. * Otherwise, null if the decimal doesn't fit within maxPrecision / maxScale or rounding * caused a result that exceeds the specified limits or MAX_PRECISION integer digits. */ @HiveDecimalVersionV1 public static HiveDecimal enforcePrecisionScale( HiveDecimal dec, int maxPrecision, int maxScale) { if (maxPrecision < 1 || maxPrecision > MAX_PRECISION) { throw new IllegalArgumentException(STRING_ENFORCE_PRECISION_OUT_OF_RANGE); } if (maxScale < 0 || maxScale > HiveDecimal.MAX_SCALE) { throw new IllegalArgumentException(STRING_ENFORCE_SCALE_OUT_OF_RANGE); } if (maxPrecision < maxScale) { throw new IllegalArgumentException(STRING_ENFORCE_SCALE_LESS_THAN_EQUAL_PRECISION); } if (dec == null) { return null; } FastCheckPrecisionScaleStatus status = dec.fastCheckPrecisionScale( maxPrecision, maxScale); switch (status) { case NO_CHANGE: return dec; case OVERFLOW: return null; case UPDATE_SCALE_DOWN: { HiveDecimal result = new HiveDecimal(); if (!dec.fastUpdatePrecisionScale( maxPrecision, maxScale, status, result)) { return null; } return result; } default: throw new RuntimeException("Unknown fast decimal check precision and scale status " + status); } } //----------------------------------------------------------------------------------------------- // Validation methods. //----------------------------------------------------------------------------------------------- /** * Throws an exception if the current decimal value is invalid. */ @HiveDecimalVersionV2 public void validate() { if (!fastIsValid()) { fastRaiseInvalidException(); } } }