001/** 002 * Copyright (c) 2012, 2014, Credit Suisse (Anatole Tresch), Werner Keil and others by the @author tag. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 005 * use this file except in compliance with the License. You may obtain a copy of 006 * the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 013 * License for the specific language governing permissions and limitations under 014 * the License. 015 */ 016package org.javamoney.moneta; 017 018import org.javamoney.moneta.ToStringMonetaryAmountFormat.ToStringMonetaryAmountFormatStyle; 019import org.javamoney.moneta.internal.FastMoneyAmountBuilder; 020import org.javamoney.moneta.spi.DefaultNumberValue; 021import org.javamoney.moneta.spi.MonetaryConfig; 022import org.javamoney.moneta.spi.MoneyUtils; 023 024import javax.money.*; 025import javax.money.format.MonetaryAmountFormat; 026 027import java.io.Serializable; 028import java.math.BigDecimal; 029import java.util.Objects; 030import java.util.logging.Level; 031import java.util.logging.Logger; 032 033/** 034 * <code>long</code> based implementation of {@link MonetaryAmount}.This class internally uses a 035 * single long number as numeric representation, which basically is interpreted as minor units.<p> 036 * It suggested to have a performance advantage of a 10-15 times faster compared to {@link Money}, 037 * which internally uses {@link BigDecimal}. Nevertheless this comes with a price of less precision. 038 * As an example performing the following calculation one million times, results in slightly 039 * different results: 040 * </p> 041 * <pre><code> 042 * Money money1 = money1.add(Money.of(EURO, 1234567.3444)); 043 * money1 = money1.subtract(Money.of(EURO, 232323)); 044 * money1 = money1.multiply(3.4); 045 * money1 = money1.divide(5.456); 046 * </code></pre> 047 * <p> 048 * Executed one million (1000000) times this results in {@code EUR 1657407.962529182}, calculated in 049 * 3680 ms, or roughly 3ns/loop. 050 * <p> 051 * whereas 052 * </p> 053 * <pre><code> 054 * FastMoney money1 = money1.add(FastMoney.of(EURO, 1234567.3444)); 055 * money1 = money1.subtract(FastMoney.of(EURO, 232323)); 056 * money1 = money1.multiply(3.4); 057 * money1 = money1.divide(5.456); 058 * </code></pre> 059 * <p> 060 * executed one million (1000000) times results in {@code EUR 1657407.96251}, calculated in 179 ms, 061 * which is less than 1ns/loop. 062 * </p><p> 063 * Also note than mixing up types my drastically change the performance behavior. E.g. replacing the 064 * code above with the following: * 065 * </p> 066 * <pre><code> 067 * FastMoney money1 = money1.add(Money.of(EURO, 1234567.3444)); 068 * money1 = money1.subtract(FastMoney.of(EURO, 232323)); 069 * money1 = money1.multiply(3.4); 070 * money1 = money1.divide(5.456); 071 * </code></pre> 072 * <p> 073 * executed one million (1000000) times may execute significantly longer, since monetary amount type 074 * conversion is involved. 075 * </p><p> 076 * Basically, when mixing amount implementations, the performance of the amount, on which most of 077 * the operations are operated, has the most significant impact on the overall performance behavior. 078 * 079 * @author Anatole Tresch 080 * @author Werner Keil 081 * @version 0.5.2 082 */ 083public final class FastMoney implements MonetaryAmount, Comparable<MonetaryAmount>, Serializable { 084 085 private static final long serialVersionUID = 1L; 086 087 /** 088 * The logger used. 089 */ 090 private static final Logger LOG = Logger.getLogger(FastMoney.class.getName()); 091 092 /** 093 * The currency of this amount. 094 */ 095 private final CurrencyUnit currency; 096 097 /** 098 * The numeric part of this amount. 099 */ 100 private final long number; 101 102 /** 103 * The current scale represented by the number. 104 */ 105 private static final int SCALE = 5; 106 107 /** 108 * the {@link MonetaryContext} used by this instance, e.g. on division. 109 */ 110 private static final MonetaryContext MONETARY_CONTEXT = 111 MonetaryContextBuilder.of(FastMoney.class).setMaxScale(SCALE).setFixedScale(true).setPrecision(19).build(); 112 113 /** 114 * Maximum possible value supported, using XX (no currency). 115 */ 116 public static final FastMoney MAX_VALUE = new FastMoney(Long.MAX_VALUE, MonetaryCurrencies.getCurrency("XXX")); 117 /** 118 * Maximum possible numeric value supported. 119 */ 120 private static final BigDecimal MAX_BD = MAX_VALUE.getBigDecimal(); 121 /** 122 * Minimum possible value supported, using XX (no currency). 123 */ 124 public static final FastMoney MIN_VALUE = new FastMoney(Long.MIN_VALUE, MonetaryCurrencies.getCurrency("XXX")); 125 /** 126 * Minimum possible numeric value supported. 127 */ 128 private static final BigDecimal MIN_BD = MIN_VALUE.getBigDecimal(); 129 130 131 /** 132 * Creates a new instance os {@link FastMoney}. 133 * 134 * @param currency the currency, not null. 135 * @param number the amount, not null. 136 */ 137 private FastMoney(Number number, CurrencyUnit currency, boolean allowInternalRounding) { 138 Objects.requireNonNull(currency, "Currency is required."); 139 this.currency = currency; 140 Objects.requireNonNull(number, "Number is required."); 141 this.number = getInternalNumber(number, allowInternalRounding); 142 } 143 144 /** 145 * Creates a new instance os {@link FastMoney}. 146 * 147 * @param currency the currency, not null. 148 * @param numberValue the numeric value, not null. 149 */ 150 private FastMoney(NumberValue numberValue, CurrencyUnit currency, boolean allowInternalRounding) { 151 Objects.requireNonNull(currency, "Currency is required."); 152 this.currency = currency; 153 Objects.requireNonNull(numberValue, "Number is required."); 154 this.number = getInternalNumber(numberValue.numberValue(BigDecimal.class), allowInternalRounding); 155 } 156 157 /** 158 * Creates a new instance os {@link FastMoney}. 159 * 160 * @param number The internal number value 161 * @param currency the currency, not null. 162 */ 163 private FastMoney(long number, CurrencyUnit currency) { 164 Objects.requireNonNull(currency, "Currency is required."); 165 this.currency = currency; 166 this.number = number; 167 } 168 169 /** 170 * Returns the amount’s currency, modelled as {@link CurrencyUnit}. 171 * Implementations may co-variantly change the return type to a more 172 * specific implementation of {@link CurrencyUnit} if desired. 173 * 174 * @return the currency, never {@code null} 175 * @see javax.money.MonetaryAmount#getCurrency() 176 */ 177 @Override 178 public CurrencyUnit getCurrency() { 179 return currency; 180 } 181 182 /** 183 * Access the {@link MonetaryContext} used by this instance. 184 * 185 * @return the {@link MonetaryContext} used, never null. 186 * @see javax.money.MonetaryAmount#getMonetaryContext() 187 */ 188 @Override 189 public MonetaryContext getMonetaryContext() { 190 return MONETARY_CONTEXT; 191 } 192 193 private long getInternalNumber(Number number, boolean allowInternalRounding) { 194 BigDecimal bd = MoneyUtils.getBigDecimal(number); 195 if (!allowInternalRounding && bd.scale() > SCALE) { 196 throw new ArithmeticException(number + " can not be represented by this class, scale > " + SCALE); 197 } 198 if (bd.compareTo(MIN_BD) < 0) { 199 throw new ArithmeticException("Overflow: " + number + " < " + MIN_BD); 200 } else if (bd.compareTo(MAX_BD) > 0) { 201 throw new ArithmeticException("Overflow: " + number + " > " + MAX_BD); 202 } 203 return bd.movePointRight(SCALE).longValue(); 204 } 205 206 207 /** 208 * Static factory method for creating a new instance of {@link FastMoney}. 209 * 210 * @param currency The target currency, not null. 211 * @param numberBinding The numeric part, not null. 212 * @return A new instance of {@link FastMoney}. 213 */ 214 public static FastMoney of(NumberValue numberBinding, CurrencyUnit currency) { 215 return new FastMoney(numberBinding, currency, false); 216 } 217 218 /** 219 * Static factory method for creating a new instance of {@link FastMoney}. 220 * 221 * @param currency The target currency, not null. 222 * @param number The numeric part, not null. 223 * @return A new instance of {@link FastMoney}. 224 */ 225 public static FastMoney of(Number number, CurrencyUnit currency) { 226 return new FastMoney(number, currency, false); 227 } 228 229 /** 230 * Static factory method for creating a new instance of {@link FastMoney}. 231 * 232 * @param currencyCode The target currency as currency code. 233 * @param number The numeric part, not null. 234 * @return A new instance of {@link FastMoney}. 235 */ 236 public static FastMoney of(Number number, String currencyCode) { 237 CurrencyUnit currency = MonetaryCurrencies.getCurrency(currencyCode); 238 return of(number, currency); 239 } 240 241 /* 242 * @see java.lang.Comparable#compareTo(java.lang.Object) 243 */ 244 @Override 245 public int compareTo(MonetaryAmount o) { 246 Objects.requireNonNull(o); 247 int compare = getCurrency().getCurrencyCode().compareTo(o.getCurrency().getCurrencyCode()); 248 if (compare == 0) { 249 compare = getNumber().numberValue(BigDecimal.class).compareTo(o.getNumber().numberValue(BigDecimal.class)); 250 } 251 return compare; 252 } 253 254 /* 255 * (non-Javadoc) 256 * @see java.lang.Object#hashCode() 257 */ 258 @Override 259 public int hashCode() { 260 return Objects.hash(currency, number); 261 } 262 263 /* 264 * (non-Javadoc) 265 * @see java.lang.Object#equals(java.lang.Object) 266 */ 267 @Override 268 public boolean equals(Object obj) { 269 if (obj == this) { 270 return true; 271 } 272 if (obj instanceof FastMoney) { 273 FastMoney other = (FastMoney) obj; 274 return Objects.equals(currency, other.currency) && Objects.equals(number, other.number); 275 } 276 return false; 277 } 278 279 280 /* 281 * (non-Javadoc) 282 * @see javax.money.MonetaryAmount#abs() 283 */ 284 @Override 285 public FastMoney abs() { 286 if (this.isPositiveOrZero()) { 287 return this; 288 } 289 return this.negate(); 290 } 291 292 // Arithmetic Operations 293 294 /* 295 * (non-Javadoc) 296 * @see javax.money.MonetaryAmount#add(javax.money.MonetaryAmount) 297 */ 298 @Override 299 public FastMoney add(MonetaryAmount amount) { 300 checkAmountParameter(amount); 301 if (amount.isZero()) { 302 return this; 303 } 304 return new FastMoney(Math.addExact(this.number, getInternalNumber(amount.getNumber(), false)), getCurrency()); 305 } 306 307 private void checkAmountParameter(MonetaryAmount amount) { 308 MoneyUtils.checkAmountParameter(amount, this.currency); 309 // numeric check for overflow... 310 if (amount.getNumber().getScale() > SCALE) { 311 throw new ArithmeticException("Parameter exceeds maximal scale: " + SCALE); 312 } 313 if (amount.getNumber().getPrecision() > MAX_BD.precision()) { 314 throw new ArithmeticException("Parameter exceeds maximal precision: " + SCALE); 315 } 316 } 317 318 319 /* 320 * (non-Javadoc) 321 * @see javax.money.MonetaryAmount#divide(java.lang.Number) 322 */ 323 @Override 324 public FastMoney divide(Number divisor) { 325 checkNumber(divisor); 326 if (isOne(divisor)) { 327 return this; 328 } 329 return new FastMoney(Math.round(this.number / divisor.doubleValue()), getCurrency()); 330 } 331 332 /* 333 * (non-Javadoc) 334 * @see javax.money.MonetaryAmount#divideAndRemainder(java.lang.Number) 335 */ 336 @Override 337 public FastMoney[] divideAndRemainder(Number divisor) { 338 checkNumber(divisor); 339 if (isOne(divisor)) { 340 return new FastMoney[]{this, FastMoney.of(0, getCurrency())}; 341 } 342 BigDecimal div = MoneyUtils.getBigDecimal(divisor); 343 BigDecimal[] res = getBigDecimal().divideAndRemainder(div); 344 return new FastMoney[]{new FastMoney(res[0], getCurrency(), true), new FastMoney(res[1], getCurrency(), true)}; 345 } 346 347 /* 348 * (non-Javadoc) 349 * @see javax.money.MonetaryAmount#divideToIntegralValue(java.lang.Number) 350 */ 351 @Override 352 public FastMoney divideToIntegralValue(Number divisor) { 353 checkNumber(divisor); 354 if (isOne(divisor)) { 355 return this; 356 } 357 BigDecimal div = MoneyUtils.getBigDecimal(divisor); 358 return new FastMoney(getBigDecimal().divideToIntegralValue(div), getCurrency(), false); 359 } 360 361 @Override 362 public FastMoney multiply(Number multiplicand) { 363 checkNumber(multiplicand); 364 if (isOne(multiplicand)) { 365 return this; 366 } 367 return new FastMoney(Math.multiplyExact(this.number, getInternalNumber(multiplicand, false)) / 100000L, 368 getCurrency()); 369 } 370 371 /* 372 * (non-Javadoc) 373 * @see javax.money.MonetaryAmount#negate() 374 */ 375 @Override 376 public FastMoney negate() { 377 return new FastMoney(Math.multiplyExact(this.number, -1), getCurrency()); 378 } 379 380 /* 381 * (non-Javadoc) 382 * @see javax.money.MonetaryAmount#plus() 383 */ 384 @Override 385 public FastMoney plus() { 386 if (this.number >= 0) { 387 return this; 388 } 389 return new FastMoney(Math.multiplyExact(this.number, -1), getCurrency()); 390 } 391 392 /* 393 * (non-Javadoc) 394 * @see javax.money.MonetaryAmount#subtract(javax.money.MonetaryAmount) 395 */ 396 @Override 397 public FastMoney subtract(MonetaryAmount subtrahend) { 398 checkAmountParameter(subtrahend); 399 if (subtrahend.isZero()) { 400 return this; 401 } 402 return new FastMoney(Math.subtractExact(this.number, getInternalNumber(subtrahend.getNumber(), false)), 403 getCurrency()); 404 } 405 406 /* 407 * (non-Javadoc) 408 * @see javax.money.MonetaryAmount#remainder(java.lang.Number) 409 */ 410 @Override 411 public FastMoney remainder(Number divisor) { 412 checkNumber(divisor); 413 if (isOne(divisor)) { 414 return new FastMoney(0, getCurrency()); 415 } 416 return new FastMoney(this.number % getInternalNumber(divisor, false), getCurrency()); 417 } 418 419 private boolean isOne(Number number) { 420 BigDecimal bd = MoneyUtils.getBigDecimal(number); 421 try { 422 return bd.scale() == 0 && bd.longValueExact() == 1L; 423 } catch (Exception e) { 424 // The only way to end up here is that longValueExact throws an ArithmeticException, 425 // so the amount is definitively not equal to 1. 426 return false; 427 } 428 } 429 430 /* 431 * (non-Javadoc) 432 * @see javax.money.MonetaryAmount#scaleByPowerOfTen(int) 433 */ 434 @Override 435 public FastMoney scaleByPowerOfTen(int n) { 436 return new FastMoney(getNumber().numberValue(BigDecimal.class).scaleByPowerOfTen(n), getCurrency(), true); 437 } 438 439 /* 440 * (non-Javadoc) 441 * @see javax.money.MonetaryAmount#isZero() 442 */ 443 @Override 444 public boolean isZero() { 445 return this.number == 0L; 446 } 447 448 /* 449 * (non-Javadoc) 450 * @see javax.money.MonetaryAmount#isPositive() 451 */ 452 @Override 453 public boolean isPositive() { 454 return this.number > 0L; 455 } 456 457 /* 458 * (non-Javadoc) 459 * @see javax.money.MonetaryAmount#isPositiveOrZero() 460 */ 461 @Override 462 public boolean isPositiveOrZero() { 463 return this.number >= 0L; 464 } 465 466 /* 467 * (non-Javadoc) 468 * @see javax.money.MonetaryAmount#isNegative() 469 */ 470 @Override 471 public boolean isNegative() { 472 return this.number < 0L; 473 } 474 475 /* 476 * (non-Javadoc) 477 * @see javax.money.MonetaryAmount#isNegativeOrZero() 478 */ 479 @Override 480 public boolean isNegativeOrZero() { 481 return this.number <= 0L; 482 } 483 484 /* 485 * (non-Javadoc) 486 * @see javax.money.MonetaryAmount#getScale() 487 */ 488 public int getScale() { 489 return FastMoney.SCALE; 490 } 491 492 /* 493 * (non-Javadoc) 494 * @see javax.money.MonetaryAmount#getPrecision() 495 */ 496 public int getPrecision() { 497 return getNumber().numberValue(BigDecimal.class).precision(); 498 } 499 500 /* 501 * (non-Javadoc) 502 * @see javax.money.MonetaryAmount#signum() 503 */ 504 505 @Override 506 public int signum() { 507 if (this.number < 0) { 508 return -1; 509 } 510 if (this.number == 0) { 511 return 0; 512 } 513 return 1; 514 } 515 516 /* 517 * (non-Javadoc) 518 * @see javax.money.MonetaryAmount#lessThan(javax.money.MonetaryAmount) 519 */ 520 @Override 521 public boolean isLessThan(MonetaryAmount amount) { 522 checkAmountParameter(amount); 523 return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) < 0; 524 } 525 526 /* 527 * (non-Javadoc) 528 * @see javax.money.MonetaryAmount#lessThan(java.lang.Number) 529 */ 530 public boolean isLessThan(Number number) { 531 checkNumber(number); 532 return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) < 0; 533 } 534 535 /* 536 * (non-Javadoc) 537 * @see javax.money.MonetaryAmount#lessThanOrEqualTo(javax.money.MonetaryAmount) 538 */ 539 @Override 540 public boolean isLessThanOrEqualTo(MonetaryAmount amount) { 541 checkAmountParameter(amount); 542 return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) <= 0; 543 } 544 545 /* 546 * (non-Javadoc) 547 * @see javax.money.MonetaryAmount#lessThanOrEqualTo(java.lang.Number) 548 */ 549 public boolean isLessThanOrEqualTo(Number number) { 550 checkNumber(number); 551 return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) <= 0; 552 } 553 554 /* 555 * (non-Javadoc) 556 * @see javax.money.MonetaryAmount#greaterThan(javax.money.MonetaryAmount) 557 */ 558 @Override 559 public boolean isGreaterThan(MonetaryAmount amount) { 560 checkAmountParameter(amount); 561 return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) > 0; 562 } 563 564 /* 565 * (non-Javadoc) 566 * @see javax.money.MonetaryAmount#greaterThan(java.lang.Number) 567 */ 568 public boolean isGreaterThan(Number number) { 569 checkNumber(number); 570 return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) > 0; 571 } 572 573 /* 574 * (non-Javadoc) 575 * @see javax.money.MonetaryAmount#greaterThanOrEqualTo(javax.money.MonetaryAmount ) #see 576 */ 577 @Override 578 public boolean isGreaterThanOrEqualTo(MonetaryAmount amount) { 579 checkAmountParameter(amount); 580 return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) >= 0; 581 } 582 583 /* 584 * (non-Javadoc) 585 * @see javax.money.MonetaryAmount#greaterThanOrEqualTo(java.lang.Number) 586 */ 587 public boolean isGreaterThanOrEqualTo(Number number) { 588 checkNumber(number); 589 return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) >= 0; 590 } 591 592 /* 593 * (non-Javadoc) 594 * @see javax.money.MonetaryAmount#isEqualTo(javax.money.MonetaryAmount) 595 */ 596 @Override 597 public boolean isEqualTo(MonetaryAmount amount) { 598 checkAmountParameter(amount); 599 return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) == 0; 600 } 601 602 /* 603 * (non-Javadoc) 604 * @see javax.money.MonetaryAmount#hasSameNumberAs(java.lang.Number) 605 */ 606 public boolean hasSameNumberAs(Number number) { 607 checkNumber(number); 608 try { 609 return this.number == getInternalNumber(number, false); 610 } catch (ArithmeticException e) { 611 return false; 612 } 613 } 614 615 616 /** 617 * Gets the number representation of the numeric value of this item. 618 * 619 * @return The {@link Number} represention matching best. 620 */ 621 @Override 622 public NumberValue getNumber() { 623 return new DefaultNumberValue(getBigDecimal()); 624 } 625 626 @Override 627 public String toString() { 628 return currency.toString() + ' ' + getBigDecimal(); 629 } 630 631 // Internal helper methods 632 633 /** 634 * Internal method to check for correct number parameter. 635 * 636 * @param number the number to be checked, including null.. 637 * @throws NullPointerException If the number is null 638 * @throws java.lang.ArithmeticException If the number exceeds the capabilities of this class. 639 */ 640 protected void checkNumber(Number number) { 641 Objects.requireNonNull(number, "Number is required."); 642 // numeric check for overflow... 643 if (number.longValue() > MAX_BD.longValue()) { 644 throw new ArithmeticException("Value exceeds maximal value: " + MAX_BD); 645 } 646 BigDecimal bd = MoneyUtils.getBigDecimal(number); 647 if (bd.precision() > MAX_BD.precision()) { 648 throw new ArithmeticException("Precision exceeds maximal precision: " + MAX_BD.precision()); 649 } 650 if (bd.scale() > SCALE) { 651 if (Boolean.parseBoolean(MonetaryConfig.getConfig() 652 .getOrDefault("org.javamoney.moneta.FastMoney.enforceScaleCompatibility", 653 "false"))) { 654 throw new ArithmeticException("Scale of " + bd + " exceeds maximal scale: " + SCALE); 655 } else { 656 if (LOG.isLoggable(Level.FINEST)) { 657 LOG.finest("Scale exceeds maximal scale of FastMoney (" + SCALE + 658 "), implicit rounding will be applied to " + number); 659 } 660 } 661 } 662 } 663 664 /* 665 * }(non-Javadoc) 666 * @see javax.money.MonetaryAmount#adjust(javax.money.AmountAdjuster) 667 */ 668 @Override 669 public FastMoney with(MonetaryOperator operator) { 670 Objects.requireNonNull(operator); 671 try { 672 return FastMoney.class.cast(operator.apply(this)); 673 } catch (ArithmeticException e) { 674 throw e; 675 } catch (Exception e) { 676 throw new MonetaryException("Operator failed: " + operator, e); 677 } 678 } 679 680 @Override 681 public <R> R query(MonetaryQuery<R> query) { 682 Objects.requireNonNull(query); 683 try { 684 return query.queryFrom(this); 685 } catch (MonetaryException | ArithmeticException e) { 686 throw e; 687 } catch (Exception e) { 688 throw new MonetaryException("Query failed: " + query, e); 689 } 690 } 691 692 public static FastMoney from(MonetaryAmount amount) { 693 if (FastMoney.class.isInstance(amount)) { 694 return FastMoney.class.cast(amount); 695 } 696 return new FastMoney(amount.getNumber(), amount.getCurrency(), false); 697 } 698 699 /** 700 * Obtains an instance of FastMoney from a text string such as 'EUR 25.25'. 701 * 702 * @param text the text to parse not null 703 * @return FastMoney instance 704 * @throws NullPointerException 705 * @throws NumberFormatException 706 * @throws UnknownCurrencyException 707 */ 708 public static FastMoney parse(CharSequence text) { 709 return parse(text, DEFAULT_FORMATTER); 710 } 711 712 /** 713 * Obtains an instance of FastMoney from a text using specific formatter. 714 * 715 * @param text the text to parse not null 716 * @param formatter the formatter to use not null 717 * @return FastMoney instance 718 */ 719 public static FastMoney parse(CharSequence text, MonetaryAmountFormat formatter) { 720 return from(formatter.parse(text)); 721 } 722 723 private static ToStringMonetaryAmountFormat DEFAULT_FORMATTER = ToStringMonetaryAmountFormat 724 .of(ToStringMonetaryAmountFormatStyle.FAST_MONEY); 725 726 private BigDecimal getBigDecimal() { 727 return BigDecimal.valueOf(this.number).movePointLeft(SCALE); 728 } 729 730 @Override 731 public FastMoney multiply(double amount) { 732 if (amount == 1.0) { 733 return this; 734 } 735 if (amount == 0.0) { 736 return new FastMoney(0, this.currency); 737 } 738 return new FastMoney(Math.round(this.number * amount), this.currency); 739 } 740 741 @Override 742 public FastMoney divide(long amount) { 743 if (amount == 1L) { 744 return this; 745 } 746 return new FastMoney(this.number / amount, this.currency); 747 } 748 749 @Override 750 public FastMoney divide(double number) { 751 if (number == 1.0d) { 752 return this; 753 } 754 return new FastMoney(Math.round(this.number / number), getCurrency()); 755 } 756 757 @Override 758 public FastMoney remainder(long number) { 759 return remainder(BigDecimal.valueOf(number)); 760 } 761 762 @Override 763 public FastMoney remainder(double amount) { 764 return remainder(new BigDecimal(String.valueOf(amount))); 765 } 766 767 @Override 768 public FastMoney[] divideAndRemainder(long amount) { 769 return divideAndRemainder(BigDecimal.valueOf(amount)); 770 } 771 772 @Override 773 public FastMoney[] divideAndRemainder(double amount) { 774 return divideAndRemainder(new BigDecimal(String.valueOf(amount))); 775 } 776 777 @Override 778 public FastMoney stripTrailingZeros() { 779 return this; 780 } 781 782 @Override 783 public FastMoney multiply(long multiplicand) { 784 if (multiplicand == 1) { 785 return this; 786 } 787 if (multiplicand == 0) { 788 return new FastMoney(0L, this.currency); 789 } 790 return new FastMoney(Math.multiplyExact(multiplicand, this.number), this.currency); 791 } 792 793 @Override 794 public FastMoney divideToIntegralValue(long divisor) { 795 if (divisor == 1) { 796 return this; 797 } 798 return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor)); 799 } 800 801 @Override 802 public FastMoney divideToIntegralValue(double divisor) { 803 if (divisor == 1.0) { 804 return this; 805 } 806 return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor)); 807 } 808 809 @Override 810 public MonetaryAmountFactory<FastMoney> getFactory() { 811 return new FastMoneyAmountBuilder().setAmount(this); 812 } 813 814}