• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

unitsofmeasurement / indriya / 2303

18 Mar 2025 07:15AM UTC coverage: 71.208%. Remained the same
2303

push

circleci

web-flow
Merge pull request #436 from eamonnmcmanus/missingreturn

Add missing `return` keyword.

0 of 1 new or added line in 1 file covered. (0.0%)

3732 of 5241 relevant lines covered (71.21%)

0.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.5
/src/main/java/tech/units/indriya/function/DefaultNumberSystem.java
1
/*
2
 * Units of Measurement Reference Implementation
3
 * Copyright (c) 2005-2025, Jean-Marie Dautelle, Werner Keil, Otavio Santana.
4
 *
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without modification,
8
 * are permitted provided that the following conditions are met:
9
 *
10
 * 1. Redistributions of source code must retain the above copyright notice,
11
 *    this list of conditions and the following disclaimer.
12
 *
13
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
14
 *    and the following disclaimer in the documentation and/or other materials provided with the distribution.
15
 *
16
 * 3. Neither the name of JSR-385, Indriya nor the names of their contributors may be used to endorse or promote products
17
 *    derived from this software without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
 */
30
package tech.units.indriya.function;
31

32
import java.math.BigDecimal;
33
import java.math.BigInteger;
34
import java.math.MathContext;
35
import java.math.RoundingMode;
36
import java.util.concurrent.atomic.AtomicInteger;
37
import java.util.concurrent.atomic.AtomicLong;
38
import java.util.function.UnaryOperator;
39

40
import tech.units.indriya.spi.NumberSystem;
41

42
/**
43
 * {@link NumberSystem} implementation to support Java's built-in {@link Number}s and the
44
 * {@link RationalNumber} type.
45
 *
46
 * @author Andi Huber
47
 * @author Werner Keil
48
 * @since 2.0
49
 */
50
public class DefaultNumberSystem implements NumberSystem {
1✔
51

52
    /**
53
     *  In order of increasing number type 'widening'.
54
     */
55
    private enum NumberType {
1✔
56

57
        // integer types
58
        BYTE_BOXED(true, Byte.class, (byte)1, (byte)0),
1✔
59
        SHORT_BOXED(true, Short.class, (short)1, (short)0),
1✔
60
        INTEGER_BOXED(true, Integer.class, 1, 0),
1✔
61
        INTEGER_ATOMIC(true, AtomicInteger.class, 1, 0) {
1✔
62
            @Override boolean isZero(Number number) {
63
                return ((AtomicInteger) number).intValue() == 0;
1✔
64
            }
65
        },
66
        LONG_BOXED(true, Long.class, 1L, 0L),
1✔
67
        LONG_ATOMIC(true, AtomicLong.class, 1L, 0) {
1✔
68
            @Override boolean isZero(Number number) {
69
                return ((AtomicLong) number).longValue() == 0L;
1✔
70
            }
71
        },
72
        BIG_INTEGER(true, BigInteger.class, BigInteger.ONE, BigInteger.ZERO) {
1✔
73
            @Override boolean isZero(Number number) {
74
                return ((BigInteger) number).signum() == 0;
1✔
75
            }
76
        },
77

78
        // rational types
79
        RATIONAL(false, RationalNumber.class, RationalNumber.ONE, RationalNumber.ZERO) {
1✔
80
            @Override boolean isZero(Number number) {
81
                return ((RationalNumber) number).signum() == 0;
1✔
82
            }
83
        },
84

85
        // fractional types
86
        FLOAT_BOXED(false, Float.class, 1.f, 0.f),
1✔
87
        DOUBLE_BOXED(false, Double.class, 1.d, 0.d),
1✔
88
        BIG_DECIMAL(false, BigDecimal.class, BigDecimal.ONE, BigDecimal.ZERO) {
1✔
89
            @Override boolean isZero(Number number) {
90
                return ((BigDecimal) number).signum() == 0;
1✔
91
            }
92
        },
93

94
        ;
95
        private final boolean integerOnly;
96
        private final Class<? extends Number> type;
97
        private final Number one;
98
        private final Number zero;
99

100
        private NumberType(final boolean integerOnly, final Class<? extends Number> type,
101
                final Number one, final Number zero) {
1✔
102

103
            this.integerOnly = integerOnly;
1✔
104
            this.type = type;
1✔
105
            this.one = one;
1✔
106
            this.zero = zero;
1✔
107
        }
1✔
108

109
        /**
110
         * Whether the underlying number type can only represent integers.
111
         * <p>
112
         * If <code>false</code> it can also represent fractional numbers.
113
         */
114
        public boolean isIntegerOnly() {
115
            return integerOnly;
1✔
116
        }
117

118
        @SuppressWarnings("unused")
119
        public Class<? extends Number> getType() {
120
            return type;
×
121
        }
122

123
        // 'hardcoded' for performance reasons
124
        static NumberType valueOf(final Number number) {
125
            if(number instanceof Long) {
1✔
126
                return LONG_BOXED;
1✔
127
            }
128
            if(number instanceof AtomicLong) {
1✔
129
                return LONG_ATOMIC;
1✔
130
            }
131
            if(number instanceof Integer) {
1✔
132
                return INTEGER_BOXED;
1✔
133
            }
134
            if(number instanceof AtomicInteger) {
1✔
135
                return INTEGER_ATOMIC;
1✔
136
            }
137
            if(number instanceof Double) {
1✔
138
                return DOUBLE_BOXED;
1✔
139
            }
140
            if(number instanceof Short) {
1✔
141
                return SHORT_BOXED;
1✔
142
            }
143
            if(number instanceof Byte) {
1✔
144
                return BYTE_BOXED;
1✔
145
            }
146
            if(number instanceof Float) {
1✔
147
                return FLOAT_BOXED;
1✔
148
            }
149
            if(number instanceof BigDecimal) {
1✔
150
                return BIG_DECIMAL;
1✔
151
            }
152
            if(number instanceof BigInteger) {
1✔
153
                return BIG_INTEGER;
1✔
154
            }
155
            if(number instanceof RationalNumber) {
1✔
156
                return RATIONAL;
1✔
157
            }
158
            final String msg = String.format("Unsupported number type '%s'",
1✔
159
                    number.getClass().getName());
×
160
            throw new IllegalArgumentException(msg);
×
161
        }
162

163
        /**
164
         * Whether given {@link Number} is ZERO.
165
         * @param number - must be of type {@link #getType()}
166
         * @apiNote For class internal use only, 
167
         *      such that we have control over the number's type that gets passed in.
168
         */
169
        boolean isZero(Number number) {
170
            return zero.equals(number);
1✔
171
        }
172

173
    }
174

175
    @Override
176
    public Number add(final Number x, final Number y) {
177

178
        final NumberType type_x = NumberType.valueOf(x);
1✔
179
        final NumberType type_y = NumberType.valueOf(y);
1✔
180

181
        final boolean reorder_args = type_y.ordinal()>type_x.ordinal();
1✔
182

183
        return reorder_args
1✔
184
                ? addWideAndNarrow(type_y, y, type_x, x)
1✔
185
                : addWideAndNarrow(type_x, x, type_y, y);
1✔
186
    }
187

188
    @Override
189
    public Number subtract(final Number x, final Number y) {
190
        return add(x, negate(y));
1✔
191
    }
192

193
    @Override
194
    public Number multiply(final Number x, final Number y) {
195

196
        final NumberType type_x = NumberType.valueOf(x);
1✔
197
        final NumberType type_y = NumberType.valueOf(y);
1✔
198

199
        final boolean reorder_args = type_y.ordinal()>type_x.ordinal();
1✔
200

201
        return reorder_args
1✔
202
                ? multiplyWideAndNarrow(type_y, y, type_x, x)
1✔
203
                : multiplyWideAndNarrow(type_x, x, type_y, y);
1✔
204
    }
205

206
    @Override
207
    public Number divide(final Number x, final Number y) {
208
        return multiply(x, reciprocal(y));
1✔
209
    }
210

211
    @Override
212
    public Number[] divideAndRemainder(final Number x, final Number y, final boolean roundRemainderTowardsZero) {
213

214
        final int sign_x = signum(x);
1✔
215
        final int sign_y = signum(y);
1✔
216

217
        final int sign = sign_x * sign_y;
1✔
218
        // handle corner cases when x or y are zero
219
        if(sign == 0) {
1✔
220
            if(sign_y == 0) {
×
221
                throw new ArithmeticException("division by zero");
×
222
            }
223
            if(sign_x==0) {
×
224
                return new Number[] {0, 0};
×
225
            }
226
        }
227

228
        final Number absX = abs(x);
1✔
229
        final Number absY = abs(y);
1✔
230

231
        final NumberType type_x = NumberType.valueOf(absX);
1✔
232
        final NumberType type_y = NumberType.valueOf(absY);
1✔
233

234
        // if x and y are both integer types than we can calculate integer results,
235
        // otherwise we resort to BigDecimal
236
        final boolean yieldIntegerResult = type_x.isIntegerOnly() && type_y.isIntegerOnly();
1✔
237

238
        if(yieldIntegerResult) {
1✔
239

240
            final BigInteger integer_x = integerToBigInteger(absX);
1✔
241
            final BigInteger integer_y = integerToBigInteger(absY);
1✔
242

243
            final BigInteger[] divAndRemainder = integer_x.divideAndRemainder(integer_y);
1✔
244

245
            return applyToArray(divAndRemainder, number->copySignTo(sign, (BigInteger)number));
1✔
246

247
        } else {
248

249
            final MathContext mathContext =
1✔
250
                    new MathContext(Calculus.MATH_CONTEXT.getPrecision(), RoundingMode.FLOOR);
1✔
251

252
            final BigDecimal decimal_x = (type_x == NumberType.RATIONAL)
1✔
253
                    ? ((RationalNumber) absX).bigDecimalValue()
×
254
                            : toBigDecimal(absX);
1✔
255
            final BigDecimal decimal_y = (type_y == NumberType.RATIONAL)
1✔
256
                    ? ((RationalNumber) absY).bigDecimalValue()
×
257
                            : toBigDecimal(absY);
1✔
258

259
            final BigDecimal[] divAndRemainder = decimal_x.divideAndRemainder(decimal_y, mathContext);
1✔
260

261
            if(roundRemainderTowardsZero) {
1✔
262
                return new Number[] {
×
263
                        copySignTo(sign, divAndRemainder[0]),
×
264
                        copySignTo(sign, divAndRemainder[1].toBigInteger())};
×
265

266
            } else {
267
                return applyToArray(divAndRemainder, number->copySignTo(sign, (BigDecimal)number));
1✔
268
            }
269

270
        }
271

272
    }
273

274
    @Override
275
    public Number reciprocal(final Number number) {
276
        if(isIntegerOnly(number)) {
1✔
277
            return RationalNumber.of(BigInteger.ONE, integerToBigInteger(number));
1✔
278
        }
279
        if(number instanceof BigDecimal) {
1✔
280
            return RationalNumber.of((BigDecimal) number).reciprocal();
1✔
281
        }
282
        if(number instanceof RationalNumber) {
1✔
283
            return ((RationalNumber) number).reciprocal();
1✔
284
        }
285
        if(number instanceof Double) {
1✔
286
            return RationalNumber.of((double)number).reciprocal();
1✔
287
        }
288
        if(number instanceof Float) {
×
289
            return RationalNumber.of(number.doubleValue()).reciprocal();
×
290
        }
291
        throw unsupportedNumberType(number);
×
292
    }
293

294
    @Override
295
    public int signum(final Number number) {
296
        if(number instanceof BigInteger) {
1✔
297
            return ((BigInteger) number).signum();
×
298
        }
299
        if(number instanceof BigDecimal) {
1✔
300
            return ((BigDecimal) number).signum();
1✔
301
        }
302
        if(number instanceof RationalNumber) {
1✔
303
            return ((RationalNumber) number).signum();
×
304
        }
305
        if(number instanceof Double) {
1✔
306
            return (int)Math.signum((double)number);
1✔
307
        }
308
        if(number instanceof Float) {
1✔
309
            return (int)Math.signum((float)number);
×
310
        }
311
        if(number instanceof Long || number instanceof AtomicLong) {
1✔
312
            final long longValue = number.longValue();
×
313
            return Long.signum(longValue);
×
314
        }
315
        if(number instanceof Integer || number instanceof AtomicInteger ||
1✔
316
                number instanceof Short || number instanceof Byte) {
317
            final int intValue = number.intValue();
1✔
318
            return Integer.signum(intValue);
1✔
319
        }
320
        throw unsupportedNumberType(number);
×
321
    }
322

323
    @Override
324
    public Number abs(final Number number) {
325
        if(number instanceof BigInteger) {
1✔
326
            return ((BigInteger) number).abs();
×
327
        }
328
        if(number instanceof BigDecimal) {
1✔
329
            return ((BigDecimal) number).abs();
1✔
330
        }
331
        if(number instanceof RationalNumber) {
1✔
332
            return ((RationalNumber) number).abs();
×
333
        }
334
        if(number instanceof Double) {
1✔
335
            return Math.abs((double)number);
1✔
336
        }
337
        if(number instanceof Float) {
1✔
338
            return Math.abs((float)number);
×
339
        }
340
        if(number instanceof Long || number instanceof AtomicLong) {
1✔
341
            final long longValue = number.longValue();
1✔
342
            if(longValue == Long.MIN_VALUE) {
1✔
343
                return BigInteger.valueOf(longValue).abs(); // widen to BigInteger
×
344
            }
345
            return Math.abs(longValue);
1✔
346
        }
347
        if(number instanceof Integer || number instanceof AtomicInteger) {
1✔
348
            final int intValue = number.intValue();
1✔
349
            if(intValue == Integer.MIN_VALUE) {
1✔
350
                return Math.abs(number.longValue()); // widen to long
×
351
            }
352
            return Math.abs(intValue);
1✔
353
        }
354
        if(number instanceof Short || number instanceof Byte) {
×
NEW
355
            return Math.abs(number.intValue()); // widen to int
×
356
        }
357
        throw unsupportedNumberType(number);
×
358
    }
359

360
    @Override
361
    public Number negate(final Number number) {
362
        if(number instanceof BigInteger) {
1✔
363
            return ((BigInteger) number).negate();
1✔
364
        }
365
        if(number instanceof BigDecimal) {
1✔
366
            return ((BigDecimal) number).negate();
1✔
367
        }
368
        if(number instanceof RationalNumber) {
1✔
369
            return ((RationalNumber) number).negate();
1✔
370
        }
371
        if(number instanceof Double) {
1✔
372
            return -((double)number);
1✔
373
        }
374
        if(number instanceof Float) {
1✔
375
            return -((float)number);
1✔
376
        }
377
        if(number instanceof Long || number instanceof AtomicLong) {
1✔
378
            final long longValue = number.longValue();
1✔
379
            if(longValue == Long.MIN_VALUE) {
1✔
380
                return BigInteger.valueOf(longValue).negate(); // widen to BigInteger
×
381
            }
382
            return -longValue;
1✔
383
        }
384
        if(number instanceof Integer || number instanceof AtomicInteger) {
1✔
385
            final int intValue = number.intValue();
1✔
386
            if(intValue == Integer.MIN_VALUE) {
1✔
387
                return -number.longValue(); // widen to long
×
388
            }
389
            return -intValue;
1✔
390
        }
391
        if(number instanceof Short) {
1✔
392
            final short shortValue = (short)number;
1✔
393
            if(shortValue == Short.MIN_VALUE) {
1✔
394
                return -number.intValue(); // widen to int
×
395
            }
396
            return -shortValue;
1✔
397
        }
398
        if(number instanceof Byte) {
1✔
399
            final short byteValue = (byte)number;
1✔
400
            if(byteValue == Byte.MIN_VALUE) {
1✔
401
                return -number.intValue(); // widen to int
×
402
            }
403
            return -byteValue;
1✔
404
        }
405
        throw unsupportedNumberType(number);
×
406
    }
407

408
    @Override
409
    public Number power(final Number number, final int exponent) {
410
        if(exponent==0) {
1✔
411
            if(isZero(number)) {
×
412
                throw new ArithmeticException("0^0 is not defined");
×
413
            }
414
            return 1; // x^0 == 1, for any x!=0
×
415
        }
416
        if(exponent==1) {
1✔
417
            return number; // x^1 == x, for any x
1✔
418
        }
419
        if(number instanceof BigInteger ||
1✔
420
                number instanceof Long || number instanceof AtomicLong ||
421
                number instanceof Integer || number instanceof AtomicInteger ||
422
                number instanceof Short || number instanceof Byte) {
423
            final BigInteger bigInt = integerToBigInteger(number);
×
424
            if(exponent>0) {
×
425
                return bigInt.pow(exponent);
×
426
            }
427
            return RationalNumber.ofInteger(bigInt).pow(exponent);
×
428

429
        }
430
        if(number instanceof BigDecimal) {
1✔
431
            return ((BigDecimal) number).pow(exponent, Calculus.MATH_CONTEXT);
1✔
432
        }
433
        if(number instanceof RationalNumber) {
×
434
            ((RationalNumber) number).pow(exponent);
×
435
        }
436
        if(number instanceof Double || number instanceof Float) {
×
437
            return toBigDecimal(number).pow(exponent, Calculus.MATH_CONTEXT);
×
438
        }
439
        throw unsupportedNumberType(number);
×
440
    }
441

442
    @Override
443
    public Number exp(final Number number) {
444
        //TODO[220] this is a poor implementation, certainly we can do better using BigDecimal
445
        return Math.exp(number.doubleValue());
1✔
446
    }
447

448
    @Override
449
    public Number log(final Number number) {
450
        //TODO[220] this is a poor implementation, certainly we can do better using BigDecimal
451
        return Math.log(number.doubleValue());
1✔
452
    }
453

454
    @Override
455
    public Number narrow(final Number number) {
456

457
        //Implementation Note: for performance we stop narrowing down at 'double' or 'integer' level
458

459
        if(number instanceof Integer || number instanceof AtomicInteger ||
1✔
460
                number instanceof Short || number instanceof Byte) {
461
            return number;
1✔
462
        }
463

464
        if(number instanceof Double || number instanceof Float) {
1✔
465
            final double doubleValue = number.doubleValue();
1✔
466
            if(!Double.isFinite(doubleValue)) {
1✔
467
                throw unsupportedNumberValue(doubleValue);
1✔
468
            }
469
            if(doubleValue % 1 == 0 && !isZero(number)) {
1✔
470
                // double represents an integer other than zero
471
                return narrow(BigDecimal.valueOf(doubleValue));
1✔
472
            }
473
            return number;
1✔
474
        }
475

476
        if(isIntegerOnly(number)) {
1✔
477

478
            // number is one of {BigInteger, Long}
479

480
            final int total_bits_required = bitLengthOfInteger(number);
1✔
481

482
            // check whether we have enough bits to store the result into an int
483
            if(total_bits_required<31) {
1✔
484
                return number.intValue();
1✔
485
            }
486

487
            // check whether we have enough bits to store the result into a long
488
            if(total_bits_required<63) {
1✔
489
                return number.longValue();
1✔
490
            }
491

492
            return number; // cannot narrow down
1✔
493

494
        }
495

496
        if(number instanceof BigDecimal) {
1✔
497

498
            final BigDecimal decimal = (BigDecimal) number;
1✔
499
            // educated guess: it is more likely for the given decimal to have fractional parts, than not;
500
            // hence in order to avoid the expensive conversion attempt decimal.toBigIntegerExact() below,
501
            // we do a less expensive check first
502
            if(isFractional(decimal)) {
1✔
503
                return number; // cannot narrow to integer
1✔
504
            }
505
            try {
506
                BigInteger integer = decimal.toBigIntegerExact();
1✔
507
                return narrow(integer);
1✔
508
            } catch (ArithmeticException e) {
×
509
                return number; // cannot narrow to integer (unexpected code reach, due to isFractional(decimal) guard above)
×
510
            }
511
        }
512

513
        if(number instanceof RationalNumber) {
1✔
514

515
            final RationalNumber rational = ((RationalNumber) number);
1✔
516

517
            return rational.isInteger()
1✔
518
                    ? narrow(rational.getDividend()) // divisor is ONE
1✔
519
                            : number; // cannot narrow to integer;
1✔
520
        }
521

522
        // for any other number type just do nothing
523
        return number;
×
524
    }
525

526
    @Override
527
    public int compare(final Number x, final Number y) {
528

529
        final NumberType type_x = NumberType.valueOf(x);
1✔
530
        final NumberType type_y = NumberType.valueOf(y);
1✔
531

532
        final boolean reorder_args = type_y.ordinal()>type_x.ordinal();
1✔
533

534
        return reorder_args
1✔
535
                ? -compareWideVsNarrow(type_y, y, type_x, x)
1✔
536
                : compareWideVsNarrow(type_x, x, type_y, y);
1✔
537
    }
538

539
    @Override
540
    public boolean isZero(final Number number) {
541
        NumberType numberType = NumberType.valueOf(number);
1✔
542
        return numberType.isZero(number);
1✔
543
    }
544

545
    @Override
546
    public boolean isOne(final Number number) {
547
        NumberType numberType = NumberType.valueOf(number);
1✔
548
        return compare(numberType.one, number) == 0;
1✔
549
    }
550

551
    @Override
552
    public boolean isLessThanOne(final Number number) {
553
        NumberType numberType = NumberType.valueOf(number);
1✔
554
        return compare(numberType.one, number) > 0;
1✔
555
    }
556

557
    @Override
558
    public boolean isInteger(final Number number) {
559
        NumberType numberType = NumberType.valueOf(number);
1✔
560
        return isInteger(numberType, number);
1✔
561
    }
562

563

564
    // -- HELPER
565

566
    private IllegalArgumentException unsupportedNumberValue(final Number number) {
567
        final String msg = String.format("Unsupported number value '%s' of type '%s' in number system '%s'",
1✔
568
                "" + number,
569
                number.getClass(),
1✔
570
                this.getClass().getName());
1✔
571

572
        return new IllegalArgumentException(msg);
1✔
573
    }
574

575
    private IllegalArgumentException unsupportedNumberType(final Number number) {
576
        final String msg = String.format("Unsupported number type '%s' in number system '%s'",
×
577
                number.getClass().getName(),
×
578
                this.getClass().getName());
×
579

580
        return new IllegalArgumentException(msg);
×
581
    }
582

583
    private IllegalStateException unexpectedCodeReach() {
584
        final String msg = String.format("Implementation Error: Code was reached that is expected unreachable");
×
585
        return new IllegalStateException(msg);
×
586
    }
587

588
    /**
589
     * Whether the {@link Number}'s type can only represent integers.
590
     * <p>
591
     * If <code>false</code> it can also represent fractional numbers.
592
     * <p>
593
     * Note: this does not check whether given number represents an integer.
594
     */
595
    private boolean isIntegerOnly(final Number number) {
596
        return NumberType.valueOf(number).isIntegerOnly();
1✔
597
    }
598

599
    /**
600
     * Whether given {@link BigDecimal} has (non-zero) fractional parts.
601
     * When <code>false</code>, given {@link BigDecimal} can be converted to a {@link BigInteger}.
602
     * @implNote {@link BigDecimal#stripTrailingZeros()} creates a new {@link BigDecimal} just to do the check.
603
     * @see https://stackoverflow.com/questions/1078953/check-if-bigdecimal-is-integer-value
604
     */
605
    static boolean isFractional(final BigDecimal decimal) {
606
        // check if is ZERO first
607
        if(decimal.signum() == 0) {
1✔
608
            return false;
1✔
609
        }
610
        // check if scale <= 0; if it is, then decimal definitely has no fractional parts
611
        if(decimal.scale()<=0) {
1✔
612
            return false;
1✔
613
        }
614
        // Note: this creates a new BigDecimal instance just to check for fractional parts
615
        // (perhaps we can improve that in the future)
616
        return decimal.stripTrailingZeros().scale() > 0;
1✔
617
    }
618

619
    /**
620
     * Whether given {@link Number} represents an integer.
621
     * Optimized for when we know the {@link NumberType} in advance.
622
     */
623
    private boolean isInteger(final NumberType numberType, final Number number) {
624
        if(numberType.isIntegerOnly()) {
1✔
625
            return true; // numberType only allows integer
1✔
626
        }
627
        if(number instanceof RationalNumber) {
×
628
            return ((RationalNumber)number).isInteger();
×
629
        }
630

631
        // remaining types to check: Double, Float, BigDecimal ...
632

633
        if(number instanceof BigDecimal) {
×
634
            return !isFractional((BigDecimal)number);
×
635
        }
636
        if(number instanceof Double || number instanceof Float) {
×
637
            double doubleValue = number.doubleValue();
×
638
            // see https://stackoverflow.com/questions/15963895/how-to-check-if-a-double-value-has-no-decimal-part
639
            if (numberType.isZero(number)) return false;
×
640
            return doubleValue % 1 == 0;
×
641
        }
642
        throw unsupportedNumberType(number);
×
643
    }
644

645
    private int bitLengthOfInteger(final Number number) {
646
        if(number instanceof BigInteger) {
1✔
647
            return ((BigInteger) number).bitLength();
1✔
648
        }
649
        long long_value = number.longValue();
1✔
650

651
        if(long_value == Long.MIN_VALUE) {
1✔
652
            return 63;
×
653
        } else {
654
            int leadingZeros = Long.numberOfLeadingZeros(Math.abs(long_value));
1✔
655
            return 64-leadingZeros;
1✔
656
        }
657
    }
658

659
    private BigInteger integerToBigInteger(final Number number) {
660
        if(number instanceof BigInteger) {
1✔
661
            return (BigInteger) number;
1✔
662
        }
663
        return BigInteger.valueOf(number.longValue());
1✔
664
    }
665

666
    private BigDecimal toBigDecimal(final Number number) {
667
        if(number instanceof BigDecimal) {
1✔
668
            return (BigDecimal) number;
1✔
669
        }
670
        if(number instanceof BigInteger) {
1✔
671
            return new BigDecimal((BigInteger) number);
×
672
        }
673
        if(number instanceof Long ||
1✔
674
                number instanceof AtomicLong ||
675
                number instanceof Integer ||
676
                number instanceof AtomicInteger ||
677
                number instanceof Short ||
678
                number instanceof Byte) {
679
            return BigDecimal.valueOf(number.longValue());
1✔
680
        }
681
        if(number instanceof Double || number instanceof Float) {
×
682
            return BigDecimal.valueOf(number.doubleValue());
×
683
        }
684
        if(number instanceof RationalNumber) {
×
685
            throw unexpectedCodeReach();
×
686
            //Note: don't do that (potential precision loss)
687
            //return ((RationalNumber) number).bigDecimalValue();
688
        }
689
        throw unsupportedNumberType(number);
×
690
    }
691

692
    private Number addWideAndNarrow(
693
            final NumberType wideType, final Number wide,
694
            final NumberType narrowType, final Number narrow) {
695

696
        // avoid type-check or widening if one of the arguments is zero
697
        // https://github.com/unitsofmeasurement/indriya/issues/384
698
        if (wideType.isZero(wide)) {
1✔
699
            return narrow;
1✔
700
        } else if (narrowType.isZero(narrow)) {
1✔
701
            return wide;
1✔
702
        }
703

704
        if(wideType.isIntegerOnly()) {
1✔
705
            // at this point we know, that narrow must also be an integer-only type
706
            if(wide instanceof BigInteger) {
1✔
707
                return ((BigInteger) wide).add(integerToBigInteger(narrow));
×
708
            }
709

710
            // at this point we know, that 'wide' and 'narrow' are one of {(Atomic)Long, (Atomic)Integer, Short, Byte}
711

712
            // +1 carry, not including sign
713
            int total_bits_required = Math.max(bitLengthOfInteger(wide), bitLengthOfInteger(narrow)) + 1;
1✔
714

715
            // check whether we have enough bits to store the result into a long
716
            if(total_bits_required<63) {
1✔
717
                return wide.longValue() + narrow.longValue();
1✔
718
            }
719

720
            return integerToBigInteger(wide).add(integerToBigInteger(narrow));
1✔
721
        }
722

723
        if(wide instanceof RationalNumber) {
1✔
724

725
            // at this point we know, that narrow must either be rational or an integer-only type
726
            if(narrow instanceof RationalNumber) {
1✔
727
                return ((RationalNumber) wide).add((RationalNumber) narrow);
1✔
728
            }
729

730
            return ((RationalNumber) wide).add(
1✔
731
                    RationalNumber.ofInteger(integerToBigInteger(narrow)));
1✔
732
        }
733

734
        // at this point we know, that wide is one of {BigDecimal, Double, Float}
735

736
        if(wide instanceof BigDecimal) {
1✔
737

738
            if(narrow instanceof BigDecimal) {
1✔
739
                return ((BigDecimal) wide).add((BigDecimal) narrow, Calculus.MATH_CONTEXT);
1✔
740
            }
741

742
            if(narrow instanceof Double || narrow instanceof Float) {
1✔
743
                return ((BigDecimal) wide).add(BigDecimal.valueOf(narrow.doubleValue()), Calculus.MATH_CONTEXT);
1✔
744
            }
745

746
            if(narrow instanceof RationalNumber) {
1✔
747
                //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber
748
                return ((BigDecimal) wide).add(((RationalNumber) narrow).bigDecimalValue());
1✔
749
            }
750

751
            // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte}
752
            return ((BigDecimal) wide).add(BigDecimal.valueOf(narrow.longValue()));
1✔
753

754
        }
755

756
        // at this point we know, that wide is one of {Double, Float}
757

758
        if(narrow instanceof Double || narrow instanceof Float) {
1✔
759
            //converting to BigDecimal, because especially fractional addition is sensitive to precision loss
760
            return BigDecimal.valueOf(wide.doubleValue())
1✔
761
                .add(BigDecimal.valueOf(narrow.doubleValue()));
1✔
762
        }
763

764
        if(narrow instanceof RationalNumber) {
1✔
765
            //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber
766
            return BigDecimal.valueOf(wide.doubleValue())
×
767
                    .add(((RationalNumber) narrow).bigDecimalValue());
×
768
        }
769

770
        if(narrow instanceof BigInteger) {
1✔
771
            return BigDecimal.valueOf(wide.doubleValue())
×
772
                    .add(new BigDecimal((BigInteger) narrow));
×
773
        }
774

775
        // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte}
776
        return BigDecimal.valueOf(wide.doubleValue())
1✔
777
                .add(BigDecimal.valueOf(narrow.longValue()));
1✔
778

779
    }
780

781
    private Number multiplyWideAndNarrow(
782
            final NumberType wideType, final Number wide,
783
            final NumberType narrowType, final Number narrow) {
784
        
785
        // shortcut if any of the operands is zero.
786
        if (wideType.isZero(wide)
1✔
787
                || narrowType.isZero(narrow)) {
1✔
788
            return 0;
1✔
789
        }
790

791
        if(wideType.isIntegerOnly()) {
1✔
792
            // at this point we know, that narrow must also be an integer-only type
793
            if(wide instanceof BigInteger) {
1✔
794
                return ((BigInteger) wide).multiply(integerToBigInteger(narrow));
1✔
795
            }
796

797
            // at this point we know, that 'wide' and 'narrow' are one of {(Atomic)Long, (Atomic)Integer, Short, Byte}
798

799
            int total_bits_required = bitLengthOfInteger(wide) + bitLengthOfInteger(narrow); // not including sign
1✔
800

801
            // check whether we have enough bits to store the result into a long
802
            if(total_bits_required<63) {
1✔
803
                return wide.longValue() * narrow.longValue();
1✔
804
            }
805

806
            return integerToBigInteger(wide).multiply(integerToBigInteger(narrow));
1✔
807
        }
808

809
        if(wide instanceof RationalNumber) {
1✔
810

811
            // at this point we know, that narrow must either be rational or an integer-only type
812
            if(narrow instanceof RationalNumber) {
1✔
813
                return ((RationalNumber) wide).multiply((RationalNumber) narrow);
1✔
814
            }
815

816
            return ((RationalNumber) wide).multiply(
1✔
817
                    RationalNumber.ofInteger(integerToBigInteger(narrow)));
1✔
818
        }
819

820
        // at this point we know, that wide is one of {BigDecimal, Double, Float}
821

822
        if(wide instanceof BigDecimal) {
1✔
823

824
            if(narrow instanceof BigDecimal) {
1✔
825
                return ((BigDecimal) wide).multiply((BigDecimal) narrow, Calculus.MATH_CONTEXT);
1✔
826
            }
827

828
            if(narrow instanceof BigInteger) {
1✔
829
                return ((BigDecimal) wide).multiply(new BigDecimal((BigInteger)narrow), Calculus.MATH_CONTEXT);
1✔
830
            }
831

832
            if(narrow instanceof Double || narrow instanceof Float) {
1✔
833
                return ((BigDecimal) wide).multiply(BigDecimal.valueOf(narrow.doubleValue()), Calculus.MATH_CONTEXT);
1✔
834
            }
835

836
            if(narrow instanceof RationalNumber) {
1✔
837
                //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber
838
                return ((BigDecimal) wide).multiply(((RationalNumber) narrow).bigDecimalValue());
1✔
839
            }
840

841
            // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte}
842
            return ((BigDecimal) wide).multiply(BigDecimal.valueOf(narrow.longValue()));
1✔
843

844
        }
845

846
        // at this point we know, that wide is one of {Double, Float}
847

848
        if(narrow instanceof Double || narrow instanceof Float) {
1✔
849
            // not converting to BigDecimal, because fractional multiplication is not sensitive to precision loss
850
            return wide.doubleValue() * narrow.doubleValue();
1✔
851
        }
852

853
        if(narrow instanceof RationalNumber) {
1✔
854
            //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber
855
            return BigDecimal.valueOf(wide.doubleValue())
1✔
856
                    .multiply(((RationalNumber) narrow).bigDecimalValue());
1✔
857
        }
858

859
        if(narrow instanceof BigInteger) {
1✔
860
            return BigDecimal.valueOf(wide.doubleValue())
1✔
861
                    .multiply(new BigDecimal((BigInteger) narrow));
1✔
862
        }
863

864
        // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte}
865
        return BigDecimal.valueOf(wide.doubleValue())
1✔
866
                .multiply(BigDecimal.valueOf(narrow.longValue()));
1✔
867

868
    }
869

870
    /**
871
     * @param unusedNarrowType - currently unused (but future refactoring might use it)
872
     */
873
    private int compareWideVsNarrow(
874
            final NumberType wideType, final Number wide,
875
            final NumberType unusedNarrowType, final Number narrow) {
876

877
        if(wideType.isIntegerOnly()) {
1✔
878
            // at this point we know, that narrow must also be an integer-only type
879
            if(wide instanceof BigInteger) {
1✔
880
                return ((BigInteger) wide).compareTo(integerToBigInteger(narrow));
1✔
881
            }
882

883
            // at this point we know, that 'wide' and 'narrow' are one of {(Atomic)Long, (Atomic)Integer, Short, Byte}
884
            return Long.compare(wide.longValue(), narrow.longValue());
1✔
885
        }
886

887
        if(wide instanceof RationalNumber) {
1✔
888

889
            // at this point we know, that narrow must either be rational or an integer-only type
890
            if(narrow instanceof RationalNumber) {
1✔
891
                return ((RationalNumber) wide).compareTo((RationalNumber) narrow);
1✔
892
            }
893

894
            return ((RationalNumber) wide).compareTo(
1✔
895
                    RationalNumber.ofInteger(integerToBigInteger(narrow)));
1✔
896
        }
897

898
        // at this point we know, that wide is one of {BigDecimal, Double, Float}
899

900
        if(wide instanceof BigDecimal) {
1✔
901

902
            if(narrow instanceof BigDecimal) {
1✔
903
                return ((BigDecimal) wide).compareTo((BigDecimal) narrow);
1✔
904
            }
905

906
            if(narrow instanceof Double || narrow instanceof Float) {
1✔
907
                return ((BigDecimal) wide).compareTo(BigDecimal.valueOf(narrow.doubleValue()));
×
908
            }
909

910
            if(narrow instanceof RationalNumber) {
1✔
911
                //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber
912
                return ((BigDecimal) wide).compareTo(((RationalNumber) narrow).bigDecimalValue());
×
913
            }
914

915
            if (narrow instanceof BigInteger) {
1✔
916
                //TODO for optimization, can this be done without instantiating a new BigDecimal?
917
                return ((BigDecimal) wide).compareTo(new BigDecimal((BigInteger) narrow));
1✔
918
            }
919

920
            // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte}
921
            return ((BigDecimal) wide).compareTo(BigDecimal.valueOf(narrow.longValue()));
1✔
922

923
        }
924

925
        // at this point we know, that wide is one of {Double, Float}
926

927
        if(narrow instanceof Double || narrow instanceof Float) {
1✔
928
            return Double.compare(wide.doubleValue(), narrow.doubleValue());
1✔
929
        }
930

931
        if(narrow instanceof RationalNumber) {
1✔
932
            //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber
933
            return BigDecimal.valueOf(wide.doubleValue())
×
934
                    .compareTo(((RationalNumber) narrow).bigDecimalValue());
×
935
        }
936

937
        if(narrow instanceof BigInteger) {
1✔
938
            return BigDecimal.valueOf(wide.doubleValue())
1✔
939
                    .compareTo(new BigDecimal((BigInteger) narrow));
1✔
940
        }
941

942
        // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte}
943
        return BigDecimal.valueOf(wide.doubleValue())
1✔
944
                .compareTo(BigDecimal.valueOf(narrow.longValue()));
1✔
945

946
    }
947

948
    // only for non-zero sign
949
    private static BigInteger copySignTo(final int sign, final BigInteger absNumber) {
950
        if(sign==-1) {
1✔
951
            return absNumber.negate();
×
952
        }
953
        return absNumber;
1✔
954
    }
955

956
    // only for non-zero sign
957
    private static BigDecimal copySignTo(final int sign, final BigDecimal absNumber) {
958
        if(sign==-1) {
1✔
959
            return absNumber.negate();
×
960
        }
961
        return absNumber;
1✔
962
    }
963

964
    private static Number[] applyToArray(final Number[] array, final UnaryOperator<Number> operator) {
965
        // only ever used for length=2
966
        return new Number[] {
1✔
967
                operator.apply(array[0]),
1✔
968
                operator.apply(array[1])
1✔
969
        };
970
    }
971
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc