001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-2018, Jean-Marie Dautelle, Werner Keil, Otavio Santana. 004 * 005 * All rights reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without modification, 008 * are permitted provided that the following conditions are met: 009 * 010 * 1. Redistributions of source code must retain the above copyright notice, 011 * this list of conditions and the following disclaimer. 012 * 013 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions 014 * and the following disclaimer in the documentation and/or other materials provided with the distribution. 015 * 016 * 3. Neither the name of JSR-385, Indriya nor the names of their contributors may be used to endorse or promote products 017 * derived from this software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 021 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 023 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 026 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package tech.units.indriya.function; 031 032import java.math.BigDecimal; 033import java.math.BigInteger; 034import java.math.MathContext; 035import java.util.Objects; 036import java.util.logging.Logger; 037 038/** 039 * Mathematical helper class 040 * @author Andi Huber 041 */ 042public final class Calculus { 043 private static final String MSG_NUMBER_NON_NULL = "number cannot be null"; 044 045 private static final Logger logger = Logger.getLogger(Calculus.class.getName()); 046 047 /** 048 * The default MathContext used for BigDecimal calculus. 049 */ 050 public static final MathContext DEFAULT_MATH_CONTEXT = MathContext.DECIMAL128; 051 052 /** 053 * Exposes (non-final) the MathContext used for BigDecimal calculus. 054 */ 055 public static MathContext MATH_CONTEXT = DEFAULT_MATH_CONTEXT; 056 057 /** 058 * Converts a number to {@link BigDecimal} 059 * 060 * @param number 061 * the number to be converted 062 * @return the number converted 063 */ 064 public static BigDecimal toBigDecimal(Number number) { 065 Objects.requireNonNull(number, MSG_NUMBER_NON_NULL); 066 if(number instanceof BigDecimal) { 067 return (BigDecimal) number; 068 } 069 if(number instanceof BigInteger) { 070 return new BigDecimal((BigInteger) number); 071 } 072 if(number instanceof Double) { 073 return BigDecimal.valueOf(number.doubleValue()); 074 } 075 logger.fine(()->String.format( 076 "WARNING: possibly loosing precision, when converting from Number type '%s' to double.", 077 number.getClass().getName())); 078 return BigDecimal.valueOf(number.doubleValue()); 079 } 080 081 /** 082 * Converts a number to {@link BigInteger} 083 * 084 * @param number 085 * the number to be converted 086 * @return the number converted 087 */ 088 public static BigInteger toBigInteger(Number number) { 089 Objects.requireNonNull(number, MSG_NUMBER_NON_NULL); 090 if(number instanceof BigInteger) { 091 return (BigInteger) number; 092 } 093 if(number instanceof BigDecimal) { 094 try { 095 return ((BigDecimal) number).toBigIntegerExact(); 096 } catch (ArithmeticException e) { 097 logger.fine(()->String.format( 098 "WARNING: loosing precision, when converting from BigDecimal to BigInteger.", 099 number.getClass().getName())); 100 return ((BigDecimal) number).toBigInteger(); 101 } 102 } 103 if(number instanceof Long || number instanceof Integer || number instanceof Short || number instanceof Byte) { 104 return BigInteger.valueOf(number.longValue()); 105 } 106 logger.fine(()->String.format( 107 "WARNING: possibly loosing precision, when converting from Number type '%s' to long.", 108 number.getClass().getName())); 109 return BigInteger.valueOf(number.longValue()); 110 } 111 112 /** 113 * Returns the absolute value of {@code number} 114 * @param number 115 * @return 116 */ 117 public static Number abs(Number number) { 118 Objects.requireNonNull(number, MSG_NUMBER_NON_NULL); 119 if(number instanceof BigInteger) { 120 return ((BigInteger) number).abs(); 121 } 122 if(number instanceof BigDecimal) { 123 return ((BigDecimal) number).abs(); 124 } 125 if(number instanceof Double) { 126 return Math.abs((double)number); 127 } 128 if(number instanceof Long) { 129 return Math.abs((long)number); 130 } 131 if(number instanceof Integer) { 132 return Math.abs((int)number); 133 } 134 if(number instanceof Short) { 135 return Math.abs((short)number); 136 } 137 if(number instanceof Byte) { 138 return Math.abs((byte)number); 139 } 140 logger.fine(()->String.format( 141 "WARNING: possibly loosing precision, when converting from Number type '%s' to double.", 142 number.getClass().getName())); 143 return Math.abs(number.doubleValue()); 144 } 145 146 /** 147 * Returns the negated value of {@code number} 148 * @param number 149 * @return -number 150 */ 151 public static Number negate(Number number) { 152 Objects.requireNonNull(number, MSG_NUMBER_NON_NULL); 153 if(number instanceof BigInteger) { 154 return ((BigInteger) number).negate(); 155 } 156 if(number instanceof BigDecimal) { 157 return ((BigDecimal) number).negate(); 158 } 159 if(number instanceof Double) { 160 return -((double)number); 161 } 162 if(number instanceof Long) { 163 return -((long)number); 164 } 165 if(number instanceof Integer) { 166 return -((int)number); 167 } 168 if(number instanceof Short) { 169 return -((short)number); 170 } 171 if(number instanceof Byte) { 172 return -((byte)number); 173 } 174 logger.fine(()->String.format( 175 "WARNING: possibly loosing precision, when converting from Number type '%s' to double.", 176 number.getClass().getName())); 177 return -(number.doubleValue()); 178 } 179 180 /** 181 * 182 * @param number 183 * @return 184 */ 185 public static boolean isLessThanOne(Number number) { 186 Objects.requireNonNull(number, MSG_NUMBER_NON_NULL); 187 if(number instanceof BigInteger) { 188 return ((BigInteger) number).compareTo(BigInteger.ONE) == -1; 189 } 190 if(number instanceof BigDecimal) { 191 return ((BigDecimal) number).compareTo(BigDecimal.ONE) == -1; 192 } 193 if(number instanceof Double) { 194 return ((double)number) < 1.0; 195 } 196 if(number instanceof Long || number instanceof Integer || number instanceof Short || number instanceof Byte) { 197 return number.longValue() < 1L; 198 } 199 logger.fine(()->String.format( 200 "WARNING: possibly loosing precision, when converting from Number type '%s' to double.", 201 number.getClass().getName())); 202 return number.doubleValue() < 1.0; 203 } 204}