OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 142 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
20 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.utils;
15
 
177 ilm 16
import org.openconcerto.utils.convertor.NumberConvertor;
17
 
20 ilm 18
import java.math.BigDecimal;
19
import java.math.BigInteger;
177 ilm 20
import java.math.RoundingMode;
21
import java.util.Collections;
22
import java.util.EnumSet;
23
import java.util.Set;
20 ilm 24
import java.util.concurrent.atomic.AtomicInteger;
25
import java.util.concurrent.atomic.AtomicLong;
26
 
27
public class NumberUtils {
28
 
29
    /**
21 ilm 30
     * Test class and numerical equality. E.g. {@link BigDecimal#equals(Object)} also tests the
31
     * scale.
32
     *
33
     * @param <N> type of number.
34
     * @param n1 first number, can be <code>null</code>.
35
     * @param n2 second number, can be <code>null</code>.
36
     * @return <code>true</code> if <code>n1</code> and <code>n2</code> have the same class and are
37
     *         numerically equal.
38
     * @see #areNumericallyEqual(Number, Number)
39
     */
40
    static public final <N extends Number> boolean areEqual(final N n1, final N n2) {
41
        if (n1 == null && n2 == null)
42
            return true;
43
        if (n1 == null || n2 == null)
44
            return false;
45
        final Class<? extends Number> n1Class = n1.getClass();
46
        if (n1Class != n2.getClass())
47
            return false;
48
        // Atomic* don't implement equals()
49
        if (n1Class == AtomicInteger.class || n1Class == AtomicLong.class)
50
            return n1.longValue() == n2.longValue();
51
        else if (n1Class == BigDecimal.class)
52
            return ((BigDecimal) n1).compareTo((BigDecimal) n2) == 0;
53
        else
54
            return n1.equals(n2);
55
    }
56
 
57
    /**
58
     * Test numerical equality (but ignore class).
59
     *
60
     * @param n1 first number, can be <code>null</code>.
61
     * @param n2 second number, can be <code>null</code>.
62
     * @return <code>true</code> if <code>n1</code> and <code>n2</code> are numerically equal.
63
     * @see #compare(Number, Number)
64
     */
65
    static public final boolean areNumericallyEqual(final Number n1, final Number n2) {
66
        if (n1 == null && n2 == null)
67
            return true;
68
        if (n1 == null || n2 == null)
69
            return false;
70
        return compare(n1, n2) == 0;
71
    }
72
 
73
    /**
74
     * Compare two arbitrary numbers.
75
     *
76
     * @param n1 first number, not <code>null</code>.
77
     * @param n2 second number, not <code>null</code>.
78
     * @return a negative integer, zero, or a positive integer as n1 is less than, equal to, or
79
     *         greater than n2.
80
     * @see Comparable#compareTo(Object)
81
     */
82
    static public final int compare(final Number n1, final Number n2) {
83
        Class<? extends Number> biggerClass = getWiderClass(n1, n2);
84
        // Atomic* aren't Comparable
85
        if (biggerClass == AtomicInteger.class)
86
            biggerClass = Integer.class;
87
        else if (biggerClass == AtomicLong.class)
88
            biggerClass = Long.class;
89
        return compare(n1, n2, biggerClass);
90
    }
91
 
92
    static private final <N extends Number> int compare(final Number n1, final Number n2, Class<N> clazz) {
93
        final N n1Converted = NumberConvertor.convertExact(n1, clazz);
94
        final N n2Converted = NumberConvertor.convertExact(n2, clazz);
95
        @SuppressWarnings("unchecked")
96
        final Comparable<N> comparable = (Comparable<N>) n1Converted;
97
        return comparable.compareTo(n2Converted);
98
    }
99
 
100
    /**
101
     * Return a class wide enough for both numbers. E.g. for Integer and Short, Integer ; for
102
     * BigInteger and Float, BigDecimal.
103
     *
104
     * @param n1 first number, not <code>null</code>.
105
     * @param n2 second number, not <code>null</code>.
106
     * @return a class wide enough for both numbers.
107
     * @see NumberConvertor#convertExact(Number, Class)
108
     */
109
    static public final Class<? extends Number> getWiderClass(final Number n1, final Number n2) {
110
        final Class<? extends Number> n1Class = n1.getClass();
111
        final Class<? extends Number> n2Class = n2.getClass();
112
        if (n1Class == n2Class)
113
            return n1Class;
114
        if (n1Class == BigDecimal.class || n2Class == BigDecimal.class)
115
            return BigDecimal.class;
116
 
117
        final boolean n1isFloat = n1Class == Float.class || n1Class == Double.class;
118
        final boolean n2isFloat = n2Class == Float.class || n2Class == Double.class;
119
        if (n1isFloat && n2isFloat) {
120
            // since classes are different, at least one is Double
121
            return Double.class;
122
        } else if (n1isFloat || n2isFloat) {
123
            // the only class (except the already handled BigDecimal) that can overflow in a Double
124
            // is BigInteger
125
            if (n1Class == BigInteger.class || n2Class == BigInteger.class)
126
                return BigDecimal.class;
127
            else
128
                return Double.class;
129
        }
130
 
131
        // integers or BigInteger
132
        if (n1Class == BigInteger.class || n2Class == BigInteger.class)
133
            return BigInteger.class;
134
        else if (n1Class == Long.class || n2Class == Long.class || n1Class == AtomicLong.class || n2Class == AtomicLong.class)
135
            return Long.class;
136
        else if (n1Class == Integer.class || n2Class == Integer.class || n1Class == AtomicInteger.class || n2Class == AtomicInteger.class)
137
            return Integer.class;
138
        else if (n1Class == Short.class || n2Class == Short.class)
139
            return Short.class;
140
        else if (n1Class == Byte.class || n2Class == Byte.class)
141
            return Byte.class;
142
        else
143
            throw new IllegalStateException("Unknown classes " + n1Class + " / " + n2Class);
144
    }
145
 
146
    /**
20 ilm 147
     * Whether <code>n</code> has a non-zero fractional part.
148
     *
149
     * @param n a number.
150
     * @return <code>true</code> if there is a non-zero fractional part, e.g. <code>true</code> for
151
     *         1.3d and <code>false</code> for <code>new BigDecimal("1.00")</code>.
152
     */
153
    static public final boolean hasFractionalPart(Number n) {
154
        if (n instanceof Integer || n instanceof Long || n instanceof Short || n instanceof Byte || n instanceof BigInteger || n instanceof AtomicLong || n instanceof AtomicInteger)
155
            return false;
156
 
157
        final BigDecimal bd;
158
        if (n instanceof BigDecimal)
159
            bd = (BigDecimal) n;
160
        else if (n instanceof Double || n instanceof Float)
161
            bd = new BigDecimal(n.doubleValue());
162
        else
163
            bd = new BigDecimal(n.toString());
164
        return DecimalUtils.decimalDigits(bd) > 0;
165
    }
166
 
167
    static final int MAX_LONG_LENGTH = String.valueOf(Long.MAX_VALUE).length();
168
 
169
    static public final int intDigits(final long l) {
170
        final long x = Math.abs(l);
171
        long p = 10;
172
        int i = 1;
173
        while (x >= p && i < MAX_LONG_LENGTH) {
174
            p = 10 * p;
175
            i++;
176
        }
177
        return i;
178
    }
179
 
180
    /**
181
     * The number of digits of the integer part in decimal representation.
182
     *
183
     * @param n a number, e.g. 123.45.
184
     * @return the number of digits of the integer part, e.g. 3.
185
     */
186
    static public final int intDigits(Number n) {
187
        if (n instanceof Integer || n instanceof Long || n instanceof Short || n instanceof Byte || n instanceof AtomicLong || n instanceof AtomicInteger)
188
            return intDigits(n.longValue());
189
 
190
        final BigDecimal bd;
191
        if (n instanceof BigDecimal)
192
            bd = (BigDecimal) n;
193
        else if (n instanceof BigInteger)
194
            bd = new BigDecimal((BigInteger) n);
195
        else if (n instanceof Double || n instanceof Float)
196
            bd = new BigDecimal(n.doubleValue());
197
        else
198
            bd = new BigDecimal(n.toString());
199
        return DecimalUtils.intDigits(bd);
200
    }
201
 
202
    /**
203
     * High precision divide.
204
     *
205
     * @param n the dividend.
206
     * @param d the divisor.
207
     * @return <code>n / d</code>.
208
     * @see DecimalUtils#HIGH_PRECISION
209
     */
210
    static public Number divide(Number n, double d) {
211
        if (d == 1)
212
            return n;
213
        if (n instanceof BigDecimal) {
214
            return ((BigDecimal) n).divide(new BigDecimal(d), DecimalUtils.HIGH_PRECISION);
215
        } else if (n instanceof BigInteger) {
216
            return new BigDecimal((BigInteger) n).divide(new BigDecimal(d), DecimalUtils.HIGH_PRECISION);
217
        } else {
218
            return n.doubleValue() / d;
219
        }
220
    }
80 ilm 221
 
177 ilm 222
    static public final int divideRoundUp(final int n, final int d) {
223
        return divideRound(n, d, RoundingMode.UP);
224
    }
225
 
226
    static public final int divideRoundDown(final int n, final int d) {
227
        return divideRound(n, d, RoundingMode.DOWN);
228
    }
229
 
230
    static public final int divideRoundCeiling(final int n, final int d) {
231
        return divideRound(n, d, RoundingMode.CEILING);
232
    }
233
 
234
    static public final int divideRoundFloor(final int n, final int d) {
235
        return divideRound(n, d, RoundingMode.FLOOR);
236
    }
237
 
238
    static final Set<RoundingMode> DIVIDE_SUPPORTED_MODES = Collections.unmodifiableSet(EnumSet.of(RoundingMode.UP, RoundingMode.DOWN, RoundingMode.CEILING, RoundingMode.FLOOR));
239
 
240
    static final int divideRound(final int n, final int d, final RoundingMode mode) {
241
        if (n == 0)
242
            return 0;
243
 
244
        final int mult;
245
        if (mode == RoundingMode.DOWN) {
246
            mult = 0;
247
        } else if (mode == RoundingMode.UP) {
248
            mult = signum(n) * (d - 1);
249
        } else if (mode == RoundingMode.CEILING) {
250
            // If the result is positive, behaves as for RoundingMode.UP; if negative, behaves as
251
            // for RoundingMode.DOWN
252
            mult = n > 0 ? (d - 1) : 0;
253
        } else if (mode == RoundingMode.FLOOR) {
254
            // If the result is positive, behave as for RoundingMode.DOWN; if negative, behave as
255
            // for RoundingMode.UP
256
            mult = n > 0 ? 0 : -(d - 1);
257
        } else {
258
            throw new UnsupportedOperationException();
259
        }
260
        return (n + mult) / d;
261
    }
262
 
80 ilm 263
    static public Number negate(Number n) {
264
        if (n == null)
265
            return null;
266
        final Number res;
267
        final Class<? extends Number> clazz = n.getClass();
268
        if (n instanceof BigDecimal) {
269
            res = ((BigDecimal) n).negate();
270
        } else if (n instanceof BigInteger) {
271
            res = ((BigInteger) n).negate();
272
        } else if (clazz == Short.class) {
273
            // cast needed since '-' widens to int
274
            res = (short) -n.shortValue();
275
        } else if (clazz == Integer.class) {
276
            res = -n.intValue();
277
        } else if (clazz == Long.class) {
278
            res = -n.longValue();
279
        } else if (clazz == Byte.class) {
280
            // cast needed since '-' widens to int
281
            res = (byte) -n.byteValue();
282
        } else if (clazz == AtomicInteger.class) {
283
            res = new AtomicInteger(-n.intValue());
284
        } else if (clazz == AtomicLong.class) {
285
            res = new AtomicLong(-n.longValue());
286
        } else if (clazz == Double.class) {
287
            res = -n.doubleValue();
288
        } else if (clazz == Float.class) {
289
            res = -n.floatValue();
290
        } else {
291
            // fallback for unknown class
292
            res = new BigDecimal(n.toString()).negate();
293
        }
294
        return res;
295
    }
296
 
177 ilm 297
    static public int signum(final long l) {
298
        final int res;
299
        if (l == 0)
300
            res = 0;
301
        else if (l < 0)
302
            res = -1;
303
        else
304
            res = 1;
305
        return res;
306
    }
307
 
80 ilm 308
    static public int signum(Number n) {
309
        if (n == null)
310
            throw new NullPointerException();
311
        final int res;
312
        final Class<? extends Number> clazz = n.getClass();
313
        if (n instanceof BigDecimal) {
314
            res = ((BigDecimal) n).signum();
315
        } else if (n instanceof BigInteger) {
316
            res = ((BigInteger) n).signum();
317
        } else if (clazz == Double.class) {
318
            res = (int) Math.signum(n.doubleValue());
319
        } else if (clazz == Float.class) {
320
            res = (int) Math.signum(n.floatValue());
321
        } else if (clazz == Byte.class || clazz == Short.class || clazz == Integer.class || clazz == Long.class || clazz == AtomicInteger.class || clazz == AtomicLong.class) {
177 ilm 322
            res = signum(n.longValue());
80 ilm 323
        } else {
324
            // limit overflow for unknown class
325
            res = (int) Math.signum(n.doubleValue());
326
        }
327
        return res;
328
    }
94 ilm 329
 
142 ilm 330
    // TODO use Math.toIntExact() in Java8
331
    public static int ensureInt(long l) throws ArithmeticException {
94 ilm 332
        if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
142 ilm 333
            throw new ArithmeticException("long value " + String.valueOf(l) + " cannot be cast to int without changing its value.");
94 ilm 334
        }
335
        return (int) l;
336
    }
20 ilm 337
}