001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-2020, Units of Measurement project. 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.math.RoundingMode; 036import java.util.concurrent.atomic.AtomicInteger; 037import java.util.concurrent.atomic.AtomicLong; 038import java.util.function.UnaryOperator; 039 040import tech.units.indriya.spi.NumberSystem; 041 042/** 043 * {@link NumberSystem} implementation to support Java's built-in {@link Number}s and the 044 * {@link RationalNumber} type. 045 * 046 * @author Andi Huber 047 * @since 2.0 048 */ 049public class DefaultNumberSystem implements NumberSystem { 050 051 /** 052 * In order of increasing number type 'widening'. 053 */ 054 private enum NumberType { 055 056 // integer types 057 BYTE_BOXED(true, Byte.class, (byte)1, (byte)0), 058 SHORT_BOXED(true, Short.class, (short)1, (short)0), 059 INTEGER_BOXED(true, Integer.class, 1, 0), 060 INTEGER_ATOMIC(true, AtomicInteger.class, 1, 0), 061 LONG_BOXED(true, Long.class, 1L, 0L), 062 LONG_ATOMIC(true, AtomicLong.class, 1L, 0), 063 BIG_INTEGER(true, BigInteger.class, BigInteger.ONE, BigInteger.ZERO), 064 065 // rational types 066 RATIONAL(false, RationalNumber.class, RationalNumber.ONE, RationalNumber.ZERO), 067 068 // fractional types 069 FLOAT_BOXED(false, Float.class, 1.f, 0.f), 070 DOUBLE_BOXED(false, Double.class, 1.d, 0.d), 071 BIG_DECIMAL(false, BigDecimal.class, BigDecimal.ONE, BigDecimal.ZERO), 072 073 ; 074 private final boolean integerOnly; 075 private final Class<? extends Number> type; 076 private final Number one; 077 private final Number zero; 078 079 private NumberType(boolean integerOnly, Class<? extends Number> type, 080 Number one, Number zero) { 081 082 this.integerOnly = integerOnly; 083 this.type = type; 084 this.one = one; 085 this.zero = zero; 086 } 087 088 public boolean isIntegerOnly() { 089 return integerOnly; 090 } 091 092 @SuppressWarnings("unused") 093 public Class<? extends Number> getType() { 094 return type; 095 } 096 097 // 'hardcoded' for performance reasons 098 static NumberType valueOf(Number number) { 099 if(number instanceof Long) { 100 return LONG_BOXED; 101 } 102 if(number instanceof AtomicLong) { 103 return LONG_ATOMIC; 104 } 105 if(number instanceof Integer) { 106 return INTEGER_BOXED; 107 } 108 if(number instanceof AtomicInteger) { 109 return INTEGER_ATOMIC; 110 } 111 if(number instanceof Double) { 112 return DOUBLE_BOXED; 113 } 114 if(number instanceof Short) { 115 return SHORT_BOXED; 116 } 117 if(number instanceof Byte) { 118 return BYTE_BOXED; 119 } 120 if(number instanceof Float) { 121 return FLOAT_BOXED; 122 } 123 if(number instanceof BigDecimal) { 124 return BIG_DECIMAL; 125 } 126 if(number instanceof BigInteger) { 127 return BIG_INTEGER; 128 } 129 if(number instanceof RationalNumber) { 130 return RATIONAL; 131 } 132 final String msg = String.format("Unsupported number type '%s'", 133 number.getClass().getName()); 134 throw new IllegalArgumentException(msg); 135 } 136 137 } 138 139 @Override 140 public Number add(Number x, Number y) { 141 142 final NumberType type_x = NumberType.valueOf(x); 143 final NumberType type_y = NumberType.valueOf(y); 144 145 final boolean reorder_args = type_y.ordinal()>type_x.ordinal(); 146 147 return reorder_args 148 ? addWideAndNarrow(type_y, y, type_x, x) 149 : addWideAndNarrow(type_x, x, type_y, y); 150 } 151 152 @Override 153 public Number subtract(Number x, Number y) { 154 return add(x, negate(y)); 155 } 156 157 @Override 158 public Number multiply(Number x, Number y) { 159 160 final NumberType type_x = NumberType.valueOf(x); 161 final NumberType type_y = NumberType.valueOf(y); 162 163 final boolean reorder_args = type_y.ordinal()>type_x.ordinal(); 164 165 return reorder_args 166 ? multiplyWideAndNarrow(type_y, y, type_x, x) 167 : multiplyWideAndNarrow(type_x, x, type_y, y); 168 } 169 170 @Override 171 public Number divide(Number x, Number y) { 172 return multiply(x, reciprocal(y)); 173 } 174 175 @Override 176 public Number[] divideAndRemainder(Number x, Number y, boolean roundRemainderTowardsZero) { 177 178 final int sign_x = signum(x); 179 final int sign_y = signum(y); 180 181 final int sign = sign_x * sign_y; 182 // handle corner cases when x or y are zero 183 if(sign == 0) { 184 if(sign_y == 0) { 185 throw new ArithmeticException("division by zero"); 186 } 187 if(sign_x==0) { 188 return new Number[] {0, 0}; 189 } 190 } 191 192 final Number absX = abs(x); 193 final Number absY = abs(y); 194 195 final NumberType type_x = NumberType.valueOf(absX); 196 final NumberType type_y = NumberType.valueOf(absY); 197 198 // if x and y are both integer types than we can calculate integer results, 199 // otherwise we resort to BigDecimal 200 final boolean yieldIntegerResult = type_x.isIntegerOnly() && type_y.isIntegerOnly(); 201 202 if(yieldIntegerResult) { 203 204 final BigInteger integer_x = integerToBigInteger(absX); 205 final BigInteger integer_y = integerToBigInteger(absY); 206 207 final BigInteger[] divAndRemainder = integer_x.divideAndRemainder(integer_y); 208 209 return applyToArray(divAndRemainder, number->copySignTo(sign, (BigInteger)number)); 210 211 } else { 212 213 final MathContext mathContext = 214 new MathContext(Calculus.MATH_CONTEXT.getPrecision(), RoundingMode.FLOOR); 215 216 final BigDecimal decimal_x = (type_x == NumberType.RATIONAL) 217 ? ((RationalNumber) absX).bigDecimalValue() 218 : toBigDecimal(absX); 219 final BigDecimal decimal_y = (type_y == NumberType.RATIONAL) 220 ? ((RationalNumber) absY).bigDecimalValue() 221 : toBigDecimal(absY); 222 223 final BigDecimal[] divAndRemainder = decimal_x.divideAndRemainder(decimal_y, mathContext); 224 225 if(roundRemainderTowardsZero) { 226 return new Number[] { 227 copySignTo(sign, divAndRemainder[0]), 228 copySignTo(sign, divAndRemainder[1].toBigInteger())}; 229 230 } else { 231 return applyToArray(divAndRemainder, number->copySignTo(sign, (BigDecimal)number)); 232 } 233 234 } 235 236 } 237 238 @Override 239 public Number reciprocal(Number number) { 240 if(isIntegerOnly(number)) { 241 return RationalNumber.of(BigInteger.ONE, integerToBigInteger(number)); 242 } 243 if(number instanceof BigDecimal) { 244 return RationalNumber.of((BigDecimal) number).reciprocal(); 245 } 246 if(number instanceof RationalNumber) { 247 return ((RationalNumber) number).reciprocal(); 248 } 249 if(number instanceof Double) { 250 return RationalNumber.of((double)number).reciprocal(); 251 } 252 if(number instanceof Float) { 253 return RationalNumber.of(number.doubleValue()).reciprocal(); 254 } 255 throw unsupportedNumberType(number); 256 } 257 258 @Override 259 public int signum(Number number) { 260 if(number instanceof BigInteger) { 261 return ((BigInteger) number).signum(); 262 } 263 if(number instanceof BigDecimal) { 264 return ((BigDecimal) number).signum(); 265 } 266 if(number instanceof RationalNumber) { 267 return ((RationalNumber) number).signum(); 268 } 269 if(number instanceof Double) { 270 return (int)Math.signum((double)number); 271 } 272 if(number instanceof Float) { 273 return (int)Math.signum((float)number); 274 } 275 if(number instanceof Long || number instanceof AtomicLong) { 276 final long longValue = number.longValue(); 277 return Long.signum(longValue); 278 } 279 if(number instanceof Integer || number instanceof AtomicInteger || 280 number instanceof Short || number instanceof Byte) { 281 final int intValue = number.intValue(); 282 return Integer.signum(intValue); 283 } 284 throw unsupportedNumberType(number); 285 } 286 287 @Override 288 public Number abs(Number number) { 289 if(number instanceof BigInteger) { 290 return ((BigInteger) number).abs(); 291 } 292 if(number instanceof BigDecimal) { 293 return ((BigDecimal) number).abs(); 294 } 295 if(number instanceof RationalNumber) { 296 return ((RationalNumber) number).abs(); 297 } 298 if(number instanceof Double) { 299 return Math.abs((double)number); 300 } 301 if(number instanceof Float) { 302 return Math.abs((float)number); 303 } 304 if(number instanceof Long || number instanceof AtomicLong) { 305 final long longValue = number.longValue(); 306 if(longValue == Long.MIN_VALUE) { 307 return BigInteger.valueOf(longValue).abs(); // widen to BigInteger 308 } 309 return Math.abs(longValue); 310 } 311 if(number instanceof Integer || number instanceof AtomicInteger) { 312 final int intValue = number.intValue(); 313 if(intValue == Integer.MIN_VALUE) { 314 return Math.abs(number.longValue()); // widen to long 315 } 316 return Math.abs(intValue); 317 } 318 if(number instanceof Short || number instanceof Byte) { 319 Math.abs(number.intValue()); // widen to int 320 } 321 throw unsupportedNumberType(number); 322 } 323 324 @Override 325 public Number negate(Number number) { 326 if(number instanceof BigInteger) { 327 return ((BigInteger) number).negate(); 328 } 329 if(number instanceof BigDecimal) { 330 return ((BigDecimal) number).negate(); 331 } 332 if(number instanceof RationalNumber) { 333 return ((RationalNumber) number).negate(); 334 } 335 if(number instanceof Double) { 336 return -((double)number); 337 } 338 if(number instanceof Float) { 339 return -((float)number); 340 } 341 if(number instanceof Long || number instanceof AtomicLong) { 342 final long longValue = number.longValue(); 343 if(longValue == Long.MIN_VALUE) { 344 return BigInteger.valueOf(longValue).negate(); // widen to BigInteger 345 } 346 return -longValue; 347 } 348 if(number instanceof Integer || number instanceof AtomicInteger) { 349 final int intValue = number.intValue(); 350 if(intValue == Integer.MIN_VALUE) { 351 return -number.longValue(); // widen to long 352 } 353 return -intValue; 354 } 355 if(number instanceof Short) { 356 final short shortValue = (short)number; 357 if(shortValue == Short.MIN_VALUE) { 358 return -number.intValue(); // widen to int 359 } 360 return -shortValue; 361 } 362 if(number instanceof Byte) { 363 final short byteValue = (byte)number; 364 if(byteValue == Byte.MIN_VALUE) { 365 return -number.intValue(); // widen to int 366 } 367 return -byteValue; 368 } 369 throw unsupportedNumberType(number); 370 } 371 372 @Override 373 public Number power(Number number, int exponent) { 374 if(exponent==0) { 375 if(isZero(number)) { 376 throw new ArithmeticException("0^0 is not defined"); 377 } 378 return 1; // x^0 == 1, for any x!=0 379 } 380 if(exponent==1) { 381 return number; // x^1 == x, for any x 382 } 383 if(number instanceof BigInteger || 384 number instanceof Long || number instanceof AtomicLong || 385 number instanceof Integer || number instanceof AtomicInteger || 386 number instanceof Short || number instanceof Byte) { 387 final BigInteger bigInt = integerToBigInteger(number); 388 if(exponent>0) { 389 return bigInt.pow(exponent); 390 } 391 return RationalNumber.ofInteger(bigInt).pow(exponent); 392 393 } 394 if(number instanceof BigDecimal) { 395 return ((BigDecimal) number).pow(exponent, Calculus.MATH_CONTEXT); 396 } 397 if(number instanceof RationalNumber) { 398 ((RationalNumber) number).pow(exponent); 399 } 400 if(number instanceof Double || number instanceof Float) { 401 return toBigDecimal(number).pow(exponent, Calculus.MATH_CONTEXT); 402 } 403 throw unsupportedNumberType(number); 404 } 405 406 @Override 407 public Number exp(Number number) { 408 //TODO[220] this is a poor implementation, certainly we can do better using BigDecimal 409 return Math.exp(number.doubleValue()); 410 } 411 412 @Override 413 public Number log(Number number) { 414 //TODO[220] this is a poor implementation, certainly we can do better using BigDecimal 415 return Math.log(number.doubleValue()); 416 } 417 418 @Override 419 public Number narrow(Number number) { 420 421 //Implementation Note: for performance we stop narrowing down at 'double' or 'integer' level 422 423 if(number instanceof Integer || number instanceof AtomicInteger || 424 number instanceof Short || number instanceof Byte) { 425 return number; 426 } 427 428 if(number instanceof Double || number instanceof Float) { 429 final double doubleValue = number.doubleValue(); 430 if(doubleValue % 1 == 0) { 431 // double represents an integer 432 return narrow(BigDecimal.valueOf(doubleValue)); 433 } 434 return number; 435 } 436 437 if(isIntegerOnly(number)) { 438 439 // number is one of {BigInteger, Long} 440 441 final int total_bits_required = bitLengthOfInteger(number); 442 443 // check whether we have enough bits to store the result into an int 444 if(total_bits_required<31) { 445 return number.intValue(); 446 } 447 448 // check whether we have enough bits to store the result into a long 449 if(total_bits_required<63) { 450 return number.longValue(); 451 } 452 453 return number; // cannot narrow down 454 455 } 456 457 if(number instanceof BigDecimal) { 458 459 final BigDecimal decimal = ((BigDecimal) number); 460 try { 461 BigInteger integer = decimal.toBigIntegerExact(); 462 return narrow(integer); 463 } catch (ArithmeticException e) { 464 return number; // cannot narrow to integer 465 } 466 } 467 468 if(number instanceof RationalNumber) { 469 470 final RationalNumber rational = ((RationalNumber) number); 471 472 return rational.isInteger() 473 ? narrow(rational.getDividend()) // divisor is ONE 474 : number; // cannot narrow to integer; 475 } 476 477 // for any other number type just do nothing 478 return number; 479 } 480 481 @Override 482 public int compare(Number x, Number y) { 483 484 final NumberType type_x = NumberType.valueOf(x); 485 final NumberType type_y = NumberType.valueOf(y); 486 487 final boolean reorder_args = type_y.ordinal()>type_x.ordinal(); 488 489 return reorder_args 490 ? -compareWideVsNarrow(type_y, y, type_x, x) 491 : compareWideVsNarrow(type_x, x, type_y, y); 492 } 493 494 @Override 495 public boolean isZero(Number number) { 496 NumberType numberType = NumberType.valueOf(number); 497 return compare(numberType.zero, number) == 0; 498 } 499 500 @Override 501 public boolean isOne(Number number) { 502 NumberType numberType = NumberType.valueOf(number); 503 return compare(numberType.one, number) == 0; 504 } 505 506 @Override 507 public boolean isLessThanOne(Number number) { 508 NumberType numberType = NumberType.valueOf(number); 509 return compare(numberType.one, number) < 0; 510 } 511 512 @Override 513 public boolean isInteger(Number number) { 514 NumberType numberType = NumberType.valueOf(number); 515 return isInteger(numberType, number); 516 } 517 518 519 // -- HELPER 520 521 private IllegalArgumentException unsupportedNumberType(Number number) { 522 final String msg = String.format("Unsupported number type '%s' in number system '%s'", 523 number.getClass().getName(), 524 this.getClass().getName()); 525 526 return new IllegalArgumentException(msg); 527 } 528 529 private IllegalStateException unexpectedCodeReach() { 530 final String msg = String.format("Implementation Error: Code was reached that is expected unreachable"); 531 return new IllegalStateException(msg); 532 } 533 534 private boolean isIntegerOnly(Number number) { 535 return NumberType.valueOf(number).isIntegerOnly(); 536 } 537 538 private boolean isInteger(NumberType numberType, Number number) { 539 if(numberType.isIntegerOnly()) { 540 return true; // numberType only allows integer 541 } 542 if(number instanceof RationalNumber) { 543 return ((RationalNumber)number).isInteger(); 544 } 545 546 // remaining types to check: Double, Float, BigDecimal ... 547 548 if(number instanceof BigDecimal) { 549 final BigDecimal decimal = (BigDecimal)number; 550 // see https://stackoverflow.com/questions/1078953/check-if-bigdecimal-is-integer-value 551 if(decimal.scale()<=0) { 552 return true; 553 } 554 try { 555 decimal.toBigIntegerExact(); 556 return true; 557 } catch (ArithmeticException ex) { 558 return false; 559 } 560 } 561 if(number instanceof Double || number instanceof Float) { 562 double doubleValue = number.doubleValue(); 563 // see https://stackoverflow.com/questions/15963895/how-to-check-if-a-double-value-has-no-decimal-part 564 return doubleValue % 1 == 0; 565 } 566 throw unsupportedNumberType(number); 567 } 568 569 private int bitLengthOfInteger(Number number) { 570 if(number instanceof BigInteger) { 571 return ((BigInteger) number).bitLength(); 572 } 573 long long_value = number.longValue(); 574 575 if(long_value == Long.MIN_VALUE) { 576 return 63; 577 } else { 578 int leadingZeros = Long.numberOfLeadingZeros(Math.abs(long_value)); 579 return 64-leadingZeros; 580 } 581 } 582 583 private BigInteger integerToBigInteger(Number number) { 584 if(number instanceof BigInteger) { 585 return (BigInteger) number; 586 } 587 return BigInteger.valueOf(number.longValue()); 588 } 589 590 private BigDecimal toBigDecimal(Number number) { 591 if(number instanceof BigDecimal) { 592 return (BigDecimal) number; 593 } 594 if(number instanceof BigInteger) { 595 return new BigDecimal((BigInteger) number); 596 } 597 if(number instanceof Long || 598 number instanceof AtomicLong || 599 number instanceof Integer || 600 number instanceof AtomicInteger || 601 number instanceof Short || 602 number instanceof Byte) { 603 return BigDecimal.valueOf(number.longValue()); 604 } 605 if(number instanceof Double || number instanceof Float) { 606 return BigDecimal.valueOf(number.doubleValue()); 607 } 608 if(number instanceof RationalNumber) { 609 throw unexpectedCodeReach(); 610 //Note: don't do that (potential precision loss) 611 //return ((RationalNumber) number).bigDecimalValue(); 612 } 613 throw unsupportedNumberType(number); 614 } 615 616 private Number addWideAndNarrow( 617 NumberType wideType, Number wide, 618 NumberType narrowType, Number narrow) { 619 620 if(wideType.isIntegerOnly()) { 621 // at this point we know, that narrow must also be an integer-only type 622 if(wide instanceof BigInteger) { 623 return ((BigInteger) wide).add(integerToBigInteger(narrow)); 624 } 625 626 // at this point we know, that 'wide' and 'narrow' are one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 627 628 // +1 carry, not including sign 629 int total_bits_required = Math.max(bitLengthOfInteger(wide), bitLengthOfInteger(narrow)) + 1; 630 631 // check whether we have enough bits to store the result into a long 632 if(total_bits_required<63) { 633 return wide.longValue() + narrow.longValue(); 634 } 635 636 return integerToBigInteger(wide).add(integerToBigInteger(narrow)); 637 } 638 639 if(wide instanceof RationalNumber) { 640 641 // at this point we know, that narrow must either be rational or an integer-only type 642 if(narrow instanceof RationalNumber) { 643 return ((RationalNumber) wide).add((RationalNumber) narrow); 644 } 645 646 return ((RationalNumber) wide).add( 647 RationalNumber.ofInteger(integerToBigInteger(narrow))); 648 } 649 650 // at this point we know, that wide is one of {BigDecimal, Double, Float} 651 652 if(wide instanceof BigDecimal) { 653 654 if(narrow instanceof BigDecimal) { 655 return ((BigDecimal) wide).add((BigDecimal) narrow, Calculus.MATH_CONTEXT); 656 } 657 658 if(narrow instanceof Double || narrow instanceof Float) { 659 return ((BigDecimal) wide).add(BigDecimal.valueOf(narrow.doubleValue()), Calculus.MATH_CONTEXT); 660 } 661 662 if(narrow instanceof RationalNumber) { 663 //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber 664 return ((BigDecimal) wide).add(((RationalNumber) narrow).bigDecimalValue()); 665 } 666 667 // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 668 return ((BigDecimal) wide).add(BigDecimal.valueOf(narrow.longValue())); 669 670 } 671 672 // at this point we know, that wide is one of {Double, Float} 673 674 if(narrow instanceof Double || narrow instanceof Float) { 675 //converting to BigDecimal, because especially fractional addition is sensitive to precision loss 676 return BigDecimal.valueOf(wide.doubleValue()) 677 .add(BigDecimal.valueOf(narrow.doubleValue())); 678 } 679 680 if(narrow instanceof RationalNumber) { 681 //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber 682 return BigDecimal.valueOf(wide.doubleValue()) 683 .add(((RationalNumber) narrow).bigDecimalValue()); 684 } 685 686 if(narrow instanceof BigInteger) { 687 return BigDecimal.valueOf(wide.doubleValue()) 688 .add(new BigDecimal((BigInteger) narrow)); 689 } 690 691 // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 692 return BigDecimal.valueOf(wide.doubleValue()) 693 .add(BigDecimal.valueOf(narrow.longValue())); 694 695 } 696 697 private Number multiplyWideAndNarrow( 698 NumberType wideType, Number wide, 699 NumberType narrowType, Number narrow) { 700 701 if(wideType.isIntegerOnly()) { 702 // at this point we know, that narrow must also be an integer-only type 703 if(wide instanceof BigInteger) { 704 return ((BigInteger) wide).multiply(integerToBigInteger(narrow)); 705 } 706 707 // at this point we know, that 'wide' and 'narrow' are one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 708 709 int total_bits_required = bitLengthOfInteger(wide) + bitLengthOfInteger(narrow); // not including sign 710 711 // check whether we have enough bits to store the result into a long 712 if(total_bits_required<63) { 713 return wide.longValue() * narrow.longValue(); 714 } 715 716 return integerToBigInteger(wide).multiply(integerToBigInteger(narrow)); 717 } 718 719 if(wide instanceof RationalNumber) { 720 721 // at this point we know, that narrow must either be rational or an integer-only type 722 if(narrow instanceof RationalNumber) { 723 return ((RationalNumber) wide).multiply((RationalNumber) narrow); 724 } 725 726 return ((RationalNumber) wide).multiply( 727 RationalNumber.ofInteger(integerToBigInteger(narrow))); 728 } 729 730 // at this point we know, that wide is one of {BigDecimal, Double, Float} 731 732 if(wide instanceof BigDecimal) { 733 734 if(narrow instanceof BigDecimal) { 735 return ((BigDecimal) wide).multiply((BigDecimal) narrow, Calculus.MATH_CONTEXT); 736 } 737 738 if(narrow instanceof Double || narrow instanceof Float) { 739 return ((BigDecimal) wide).multiply(BigDecimal.valueOf(narrow.doubleValue()), Calculus.MATH_CONTEXT); 740 } 741 742 if(narrow instanceof RationalNumber) { 743 //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber 744 return ((BigDecimal) wide).multiply(((RationalNumber) narrow).bigDecimalValue()); 745 } 746 747 // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 748 return ((BigDecimal) wide).multiply(BigDecimal.valueOf(narrow.longValue())); 749 750 } 751 752 // at this point we know, that wide is one of {Double, Float} 753 754 if(narrow instanceof Double || narrow instanceof Float) { 755 // not converting to BigDecimal, because fractional multiplication is not sensitive to precision loss 756 return wide.doubleValue() * narrow.doubleValue(); 757 } 758 759 if(narrow instanceof RationalNumber) { 760 //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber 761 return BigDecimal.valueOf(wide.doubleValue()) 762 .multiply(((RationalNumber) narrow).bigDecimalValue()); 763 } 764 765 if(narrow instanceof BigInteger) { 766 return BigDecimal.valueOf(wide.doubleValue()) 767 .multiply(new BigDecimal((BigInteger) narrow)); 768 } 769 770 // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 771 return BigDecimal.valueOf(wide.doubleValue()) 772 .multiply(BigDecimal.valueOf(narrow.longValue())); 773 774 } 775 776 777 private int compareWideVsNarrow( 778 NumberType wideType, Number wide, 779 NumberType narrowType, Number narrow) { 780 781 782 if(wideType.isIntegerOnly()) { 783 // at this point we know, that narrow must also be an integer-only type 784 if(wide instanceof BigInteger) { 785 return ((BigInteger) wide).compareTo(integerToBigInteger(narrow)); 786 } 787 788 // at this point we know, that 'wide' and 'narrow' are one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 789 return Long.compare(wide.longValue(), narrow.longValue()); 790 } 791 792 if(wide instanceof RationalNumber) { 793 794 // at this point we know, that narrow must either be rational or an integer-only type 795 if(narrow instanceof RationalNumber) { 796 return ((RationalNumber) wide).compareTo((RationalNumber) narrow); 797 } 798 799 return ((RationalNumber) wide).compareTo( 800 RationalNumber.ofInteger(integerToBigInteger(narrow))); 801 } 802 803 // at this point we know, that wide is one of {BigDecimal, Double, Float} 804 805 if(wide instanceof BigDecimal) { 806 807 if(narrow instanceof BigDecimal) { 808 return ((BigDecimal) wide).compareTo((BigDecimal) narrow); 809 } 810 811 if(narrow instanceof Double || narrow instanceof Float) { 812 return ((BigDecimal) wide).compareTo(BigDecimal.valueOf(narrow.doubleValue())); 813 } 814 815 if(narrow instanceof RationalNumber) { 816 //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber 817 return ((BigDecimal) wide).compareTo(((RationalNumber) narrow).bigDecimalValue()); 818 } 819 820 // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 821 return ((BigDecimal) wide).compareTo(BigDecimal.valueOf(narrow.longValue())); 822 823 } 824 825 // at this point we know, that wide is one of {Double, Float} 826 827 if(narrow instanceof Double || narrow instanceof Float) { 828 return Double.compare(wide.doubleValue(), narrow.doubleValue()); 829 } 830 831 if(narrow instanceof RationalNumber) { 832 //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber 833 return BigDecimal.valueOf(wide.doubleValue()) 834 .compareTo(((RationalNumber) narrow).bigDecimalValue()); 835 } 836 837 if(narrow instanceof BigInteger) { 838 return BigDecimal.valueOf(wide.doubleValue()) 839 .compareTo(new BigDecimal((BigInteger) narrow)); 840 } 841 842 // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 843 return BigDecimal.valueOf(wide.doubleValue()) 844 .compareTo(BigDecimal.valueOf(narrow.longValue())); 845 846 } 847 848 // only for non-zero sign 849 private static BigInteger copySignTo(int sign, BigInteger absNumber) { 850 if(sign==-1) { 851 return absNumber.negate(); 852 } 853 return absNumber; 854 } 855 856 // only for non-zero sign 857 private static BigDecimal copySignTo(int sign, BigDecimal absNumber) { 858 if(sign==-1) { 859 return absNumber.negate(); 860 } 861 return absNumber; 862 } 863 864 private static Number[] applyToArray(Number[] array, UnaryOperator<Number> operator) { 865 // only ever used for length=2 866 return new Number[] { 867 operator.apply(array[0]), 868 operator.apply(array[1]) 869 }; 870 } 871 872 873}