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

com.amazon.ion.Decimal Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2007-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * or in the "license" file accompanying this file. This file 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 com.amazon.ion;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;

/**
 * An extension of {@link BigDecimal} that can represent negative zeros.
 * The primary change is the addition of {@link #isNegativeZero()}.
 * 

* WARNING: This class should not be extended by code outside of * this library. *

* This class currently does not have any behavioral difference * from {@link BigDecimal} except for the following: *

    *
  • {@link #toString()}, {@link #toEngineeringString()}, and * {@link #toPlainString()} print a negative sign if necessary. *
  • *
  • {@link #floatValue()} and {@link #doubleValue()} return negative zero * results. *
  • *
  • {@link #abs} does the right thing. *
* This class does NOT override {@link #equals} or {@link #compareTo} * so those methods cannot distinguish positive and negative zeros. *

* It also does not currently override any of the numerical methods, * but it may do so in the future. If you are concerned about * consistent treatment of negative zeros in future releases, you may wish to * use {@link #bigDecimalValue} before performing those operations. */ public class Decimal extends BigDecimal { private static final long serialVersionUID = 1L; private static final class NegativeZero extends Decimal { private static final long serialVersionUID = 1L; private NegativeZero(int scale) { super(BigInteger.ZERO, scale); } private NegativeZero(int scale, MathContext mc) { super(BigInteger.ZERO, scale, mc); } @Override public float floatValue() { float v = super.floatValue(); if (Float.compare(0f, v) <= 0) v = -1 * v; return v; } @Override public double doubleValue() { double v = super.doubleValue(); if (Double.compare(0d, v) <= 0) v = -1 * v; return v; } @Override public BigDecimal abs() { return new BigDecimal(unscaledValue(), scale()); } @Override public BigDecimal abs(MathContext mc) { return new BigDecimal(unscaledValue(), scale(), mc); } // TODO signum might break clients if -0 returns <0 instead of 0 // TODO static negate(BigDecimal) // TODO hashcode // TODO some other things: // * byte/int/longValueExact throws? // * movePointLeft/Right // * round // * scaleByPowerOfTen // * setScale // * stripTrailingZeros // * toBigIntegerExact @Override public String toString() { return '-' + super.toString(); } @Override public String toEngineeringString() { return '-' + super.toEngineeringString(); } @Override public String toPlainString() { return '-' + super.toPlainString(); } } /** * The value 0, with a scale of 0. */ public static final Decimal ZERO = new Decimal(0); /** * The value -0, with a scale of 0. */ public static final Decimal NEGATIVE_ZERO = new NegativeZero(0); /** * Efficiently determines whether an arbitary decimal value is a negative * zero. This can only be true when the value is actually a * {@link Decimal}. * * @return {@code true} if and only if the value is a negative zero. * * @throws NullPointerException if the value is {@code null}. */ public static boolean isNegativeZero(BigDecimal val) { return (val.getClass() == NegativeZero.class); } /** * Returns a "plain" {@link BigDecimal} instance, never a {@link Decimal} * subclass. As a side effect, this strips any negative-zero information. * * @param val may be null. * @return {@code null} if the given value is {@code null}. */ public static BigDecimal bigDecimalValue(BigDecimal val) { if (val == null || val.getClass() == BigDecimal.class) { return val; } return new BigDecimal(val.unscaledValue(), val.scale()); } /** * Compares two decimal values for equality, observing both precision and * negative zeros. This differs from {@link BigDecimal#equals(Object)}, * which isn't aware of negative zeros, and from * {@link BigDecimal#compareTo(BigDecimal)}, which ignores both precision * and negative zeros. * * @return {@code true} if and only if the two {@link BigDecimal} objects * have the same value, scale, and sign. * * @throws NullPointerException if either parameter is {@code null}. */ public static boolean equals(BigDecimal v1, BigDecimal v2) { return (isNegativeZero(v1) == isNegativeZero(v2) && v1.equals(v2)); } //========================================================================= /** * Returns a negative-zero decimal value, with the given number of * significant digits (zeros). * * @param scale the number of significant digits (zeros) after the decimal * point. */ public static Decimal negativeZero(int scale) { return new NegativeZero(scale); } /** * Returns a negative-zero decimal value, with the given number of * significant digits (zeros) and given context. * * @param scale the number of significant digits (zeros) after the decimal * point. */ public static Decimal negativeZero(int scale, MathContext mc) { return new NegativeZero(scale, mc); } public static Decimal valueOf(BigInteger unscaledVal, int scale) { return new Decimal(unscaledVal, scale); } public static Decimal valueOf(BigInteger unscaledVal, int scale, MathContext mc) { return new Decimal(unscaledVal, scale, mc); } public static Decimal valueOf(BigInteger val) { return new Decimal(val); } public static Decimal valueOf(BigInteger val, MathContext mc) { return new Decimal(val, mc); } public static Decimal valueOf(int val) { return new Decimal(val); } public static Decimal valueOf(int val, MathContext mc) { return new Decimal(val, mc); } public static Decimal valueOf(long val) { return new Decimal(val); } public static Decimal valueOf(long val, MathContext mc) { return new Decimal(val, mc); } public static Decimal valueOf(double val) { if (Double.compare(val, -0d) == 0) { // Simulate BigDecimal.valueOf(0d) which has scale of 1 return new NegativeZero(1); } return new Decimal(Double.toString(val)); } public static Decimal valueOf(double val, MathContext mc) { if (Double.compare(val, -0d) == 0) { return new NegativeZero(1, mc); } return new Decimal(Double.toString(val), mc); } public static Decimal valueOf(BigDecimal val) { if (val == null || val instanceof Decimal) return (Decimal) val; return new Decimal(val.unscaledValue(), val.scale()); } public static Decimal valueOf(BigDecimal val, MathContext mc) { return new Decimal(val.unscaledValue(), val.scale(), mc); } public static Decimal valueOf(String val) { boolean negative = val.startsWith("-"); Decimal ibd = new Decimal(val); if (negative && ibd.signum() == 0) { ibd = new NegativeZero(ibd.scale()); } return ibd; } public static Decimal valueOf(String val, MathContext mc) { boolean negative = val.startsWith("-"); Decimal ibd = new Decimal(val, mc); if (negative && ibd.signum() == 0) { ibd = new NegativeZero(ibd.scale(), mc); } return ibd; } //========================================================================= // Constructors are private so we have flexibility in changing // implementation of how we create negative zero. // We force the user to call static valueOf() methods instead. private Decimal(BigInteger unscaledVal, int scale) { super(unscaledVal, scale); } private Decimal(BigInteger unscaledVal, int scale, MathContext mc) { super(unscaledVal, scale, mc); } private Decimal(BigInteger val) { super(val); } private Decimal(BigInteger val, MathContext mc) { super(val, mc); } private Decimal(int val) { super(val); } private Decimal(int val, MathContext mc) { super(val, mc); } private Decimal(long val) { super(val); } private Decimal(long val, MathContext mc) { super(val, mc); } private Decimal(double val) { super(val); } private Decimal(double val, MathContext mc) { super(val, mc); } // TODO create static valueOf to check for -0 private Decimal(char[] in, int offset, int len) { super(in, offset, len); } // TODO create static valueOf to check for -0 private Decimal(char[] in, int offset, int len, MathContext mc) { super(in, offset, len, mc); } // TODO create static valueOf to check for -0 private Decimal(char[] in) { super(in); } // TODO create static valueOf to check for -0 private Decimal(char[] in, MathContext mc) { super(in, mc); } private Decimal(String val) { super(val); } private Decimal(String val, MathContext mc) { super(val, mc); } //======================================================================== public final boolean isNegativeZero() { return (getClass() == NegativeZero.class); } /** * Converts this to a "plain" {@link BigDecimal} instance, losing any * negative zeros in the process. * * @return a {@link BigDecimal}, never a {@link Decimal}. */ public final BigDecimal bigDecimalValue() { return new BigDecimal(unscaledValue(), scale()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy