Java에서 돈을 위해 사용할 데이터 유형은 무엇입니까? [닫은]


183

Java에서 어떤 데이터 유형을 돈으로 사용해야합니까?


2
수행 할 작업에 따라 다릅니다. 자세한 정보를 제공하십시오.
eversor

@eversor 다른 작업에 어떤 데이터 형식을 사용해야하는지 설명해 주시겠습니까?
questborn

1
정확하게 센트를 나타내야하는 계산을하고 있습니다.
questborn

앱이 처리해야 할 가장 큰 금액을 예언 할 수 있습니까? 그리고 당신의 계산은 간단합니까 (추가 등) 복잡한 재무 운영입니까?
eversor

답변:


133

Java에는 CurrencyISO 4217 통화 코드를 나타내는 클래스가 있습니다. BigDecimal통화 10 진수 값을 나타내는 데 가장 적합한 유형입니다.

Joda Money 는 돈을 대표하는 도서관을 제공했습니다.


5
왜 float 또는 double을 대신 사용할 수 없습니까?
Erran Morad

20
@Borat Sagdiyev 이것이 바로 이유 입니다. 또한 이것을 참조 할 수 있습니다 .
Buhake Sindi 2018 년

2
@Borat :하고있는 일을 알고 있다면 Peter Lawrey 의이 기사 를 참조하십시오 . 그러나 BigDecimals를 사용하는 것처럼 모든 반올림을 수행하는 것이 적어도 번거로운 것 같습니다.
Nathan Hughes

35
"내가 가게 통화에 사람이 사용 FLOAT를 본 적이 모든 시간을 한푼이 있다면, 나는 $ 999.997634이있을 것이다"- 빌 Karwin
콜린 Krawll

36

Money and Currency API (JSR 354)를 사용할 수 있습니다 . 프로젝트에 적절한 종속성을 추가하면이 API를 사용할 수 있습니다.

Java 8의 경우 다음 참조 구현을 종속성에 추가하십시오 pom.xml.

<dependency>
    <groupId>org.javamoney</groupId>
    <artifactId>moneta</artifactId>
    <version>1.0</version>
</dependency>

이 종속성은 전 이적으로 종속성으로 추가 javax.money:money-api됩니다.

그런 다음 API를 사용할 수 있습니다.

package com.example.money;

import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.is;

import java.util.Locale;

import javax.money.Monetary;
import javax.money.MonetaryAmount;
import javax.money.MonetaryRounding;
import javax.money.format.MonetaryAmountFormat;
import javax.money.format.MonetaryFormats;

import org.junit.Test;

public class MoneyTest {

    @Test
    public void testMoneyApi() {
        MonetaryAmount eurAmount1 = Monetary.getDefaultAmountFactory().setNumber(1.1111).setCurrency("EUR").create();
        MonetaryAmount eurAmount2 = Monetary.getDefaultAmountFactory().setNumber(1.1141).setCurrency("EUR").create();

        MonetaryAmount eurAmount3 = eurAmount1.add(eurAmount2);
        assertThat(eurAmount3.toString(), is("EUR 2.2252"));

        MonetaryRounding defaultRounding = Monetary.getDefaultRounding();
        MonetaryAmount eurAmount4 = eurAmount3.with(defaultRounding);
        assertThat(eurAmount4.toString(), is("EUR 2.23"));

        MonetaryAmountFormat germanFormat = MonetaryFormats.getAmountFormat(Locale.GERMAN);
        assertThat(germanFormat.format(eurAmount4), is("EUR 2,23") );
    }
}

직렬화 및 DB에 저장은 어떻습니까? 유선을 통해 전송하려면 어떤 형식을 사용해야합니까?
Paweł Szczur

1
오라클은 Java 9의 Java Money를 포함하여 다시 한 번 헌신했다고 믿습니다. 그러나 좋은 대답입니다. 우리는 여전히 그것을 Maven과 함께 사용할 수 있습니다
borjab

3
Java 9에 Java Money를 포함하지 않도록 Oracle에 대한 소스가 있습니까?
Abdull

25

가능한 가장 작은 값을 나타내는 정수 유형입니다. 다시 말해, 프로그램은 달러 / 유로가 아닌 센트 단위로 생각해야합니다.

이것은 GUI가 달러 / 유로로 다시 변환하는 것을 막지 않아야합니다.


돈의 양이 INT의 크기를 오버 플로우 수 있다는 것을 염두에 베어
eversor

5
@eversor 그 대부분의 응용 프로그램은 그들이 많은 경우에조차 우리의 govenrments 오버 플로우에 충분한 mone 처리로 긴 충분할 것입니다 필요가 없을 것입니다 만 20 달러를 필요가
래칫 괴물

4
@ratchetfreak 아마도 오랫동안 사용하는 것이 좋습니다.
trognanders

5
많은 은행들이 매일 $ 20,000,000의 훨씬 큰 금액의 돈을 처리합니다. 이것은 달러와 환율이 큰 엔과 같은 통화는 고려하지 않습니다. 정수 유형은 이자율과 환율 계산에 혼란 스러울 수 있지만 반올림 문제를 피하는 것이 가장 좋습니다. 그러나 응용 프로그램에 따라 64 비트 정수 유형이 필요할 수 있습니다.
Alchymist

실제로 $ 10/3 인 것처럼 반올림 오류 (3333.3 => 3333.0)는 최종 값에 큰 영향을 미치지 않습니다 (이 경우 실제 값에는 전혀 영향을 미치지 않습니다). 절대로 그렇지 않다고 가정하는 것은 위험합니다. 반올림 오류가 복잡해 지므로 사용자가 결과를보기 전에 많은 계산을 연속으로 수행하는 경우 특히 중요합니다.
Chris Browne


11

JSR 354 : 화폐 및 통화 API

JSR 354는 Money and Currency를 사용하여 포괄적 인 계산을 표현, 전송 및 수행하기위한 API를 제공합니다. 이 링크에서 다운로드 할 수 있습니다.

JSR 354 : 화폐 및 통화 API 다운로드

사양은 다음과 같이 구성됩니다.

  1. 화폐 금액 및 통화 처리를위한 API
  2. 상호 교환 가능한 구현을 지원하는 API
  3. 구현 클래스의 인스턴스를 작성하기위한 팩토리
  4. 금액 계산, 변환 및 서식 지정 기능
  5. Java 9에 포함될 예정인 Money and Currencies 작업을위한 Java API
  6. 모든 사양 클래스와 인터페이스는 javax.money. * 패키지에 있습니다.

JSR 354의 샘플 예 : Money and Currency API :

MonetaryAmount를 작성하여 콘솔에 인쇄하는 예는 다음과 같습니다.

MonetaryAmountFactory<?> amountFactory = Monetary.getDefaultAmountFactory();
MonetaryAmount monetaryAmount = amountFactory.setCurrency(Monetary.getCurrency("EUR")).setNumber(12345.67).create();
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault());
System.out.println(format.format(monetaryAmount));

참조 구현 API를 사용할 때 필요한 코드는 훨씬 간단합니다.

MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault());
System.out.println(format.format(monetaryAmount));

API는 MonetaryAmounts를 사용한 계산도 지원합니다.

MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmount otherMonetaryAmount = monetaryAmount.divide(2).add(Money.of(5, "EUR"));

통화 단위 및 통화 금액

// getting CurrencyUnits by locale
CurrencyUnit yen = MonetaryCurrencies.getCurrency(Locale.JAPAN);
CurrencyUnit canadianDollar = MonetaryCurrencies.getCurrency(Locale.CANADA);

MonetaryAmount에는 지정된 통화, 숫자 금액, 정밀도 등을 액세스 할 수있는 다양한 방법이 있습니다.

MonetaryAmount monetaryAmount = Money.of(123.45, euro);
CurrencyUnit currency = monetaryAmount.getCurrency();
NumberValue numberValue = monetaryAmount.getNumber();

int intValue = numberValue.intValue(); // 123
double doubleValue = numberValue.doubleValue(); // 123.45
long fractionDenominator = numberValue.getAmountFractionDenominator(); // 100
long fractionNumerator = numberValue.getAmountFractionNumerator(); // 45
int precision = numberValue.getPrecision(); // 5

// NumberValue extends java.lang.Number. 
// So we assign numberValue to a variable of type Number
Number number = numberValue;

반올림 연산자를 사용하여 금액을 반올림 할 수 있습니다.

CurrencyUnit usd = MonetaryCurrencies.getCurrency("USD");
MonetaryAmount dollars = Money.of(12.34567, usd);
MonetaryOperator roundingOperator = MonetaryRoundings.getRounding(usd);
MonetaryAmount roundedDollars = dollars.with(roundingOperator); // USD 12.35

MonetaryAmounts 콜렉션으로 작업 할 때 필터링, 정렬 및 그룹화를위한 유용한 유틸리티 메소드를 사용할 수 있습니다.

List<MonetaryAmount> amounts = new ArrayList<>();
amounts.add(Money.of(2, "EUR"));
amounts.add(Money.of(42, "USD"));
amounts.add(Money.of(7, "USD"));
amounts.add(Money.of(13.37, "JPY"));
amounts.add(Money.of(18, "USD"));

사용자 정의 통화 금액 조작

// A monetary operator that returns 10% of the input MonetaryAmount
// Implemented using Java 8 Lambdas
MonetaryOperator tenPercentOperator = (MonetaryAmount amount) -> {
  BigDecimal baseAmount = amount.getNumber().numberValue(BigDecimal.class);
  BigDecimal tenPercent = baseAmount.multiply(new BigDecimal("0.1"));
  return Money.of(tenPercent, amount.getCurrency());
};

MonetaryAmount dollars = Money.of(12.34567, "USD");

// apply tenPercentOperator to MonetaryAmount
MonetaryAmount tenPercentDollars = dollars.with(tenPercentOperator); // USD 1.234567

자원:

JSR 354를 사용하여 Java에서 돈과 통화 처리

Java 9 Money and Currency API 살펴보기 (JSR 354)

다음 사항도 참조 : JSR 354-통화 및 돈


이 모든 것이 좋지만 Federico가 위에서 제안한 것처럼 BigDecimal :-)) 나쁜 농담보다 느리게 보이지만 1 년 후 지금 테스트 할 것입니다 ...
kensai

6

화폐 가치를 표현 하기 위해 BigDecimal 을 사용해야합니다. 다양한 반올림 모드 를 사용할 수 있으며, 금융 애플리케이션에서 반올림 모드는 종종 법에 의해 요구되는 어려운 요구 사항입니다.



6

성능 측면에서 Moneta (Java 통화 JSR 354 구현)와 BigDecimal을 비교하기 위해 마이크로 벤치 마크 (JMH)를 수행했습니다.

놀랍게도 BigDecimal 성능은 moneta보다 우수합니다. 다음 moneta 구성을 사용했습니다.

org.javamoney.moneta.Money.defaults.precision = 19 org.javamoney.moneta.Money.defaults.roundingMode = HALF_UP

package com.despegar.bookedia.money;

import org.javamoney.moneta.FastMoney;
import org.javamoney.moneta.Money;
import org.openjdk.jmh.annotations.*;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.concurrent.TimeUnit;

@Measurement(batchSize = 5000, iterations = 10, time = 2, timeUnit =     TimeUnit.SECONDS)
@Warmup(iterations = 2)
@Threads(value = 1)
@Fork(value = 1)
@State(Scope.Benchmark)
@BenchmarkMode(Mode.Throughput)
public class BigDecimalBenchmark {

private static final Money MONEY_BASE = Money.of(1234567.3444, "EUR");
private static final Money MONEY_SUBSTRACT = Money.of(232323, "EUR");
private static final FastMoney FAST_MONEY_SUBSTRACT = FastMoney.of(232323, "EUR");
private static final FastMoney FAST_MONEY_BASE = FastMoney.of(1234567.3444, "EUR");
MathContext mc = new MathContext(10, RoundingMode.HALF_UP);

@Benchmark
public void bigdecimal_string() {
    new BigDecimal("1234567.3444").subtract(new BigDecimal("232323")).multiply(new BigDecimal("3.4"), mc).divide(new BigDecimal("5.456"), mc);
}

@Benchmark
public void bigdecimal_valueOf() {
    BigDecimal.valueOf(12345673444L, 4).subtract(BigDecimal.valueOf(232323L)).multiply(BigDecimal.valueOf(34, 1), mc).divide(BigDecimal.valueOf(5456, 3), mc);
}
@Benchmark
public void fastmoney() {
    FastMoney.of(1234567.3444, "EUR").subtract(FastMoney.of(232323, "EUR")).multiply(3.4).divide(5.456);
}

@Benchmark
public void money() {
    Money.of(1234567.3444, "EUR").subtract(Money.of(232323, "EUR")).multiply(3.4).divide(5.456);
}

@Benchmark
public void money_static(){
    MONEY_BASE.subtract(MONEY_SUBSTRACT).multiply(3.4).divide(5.456);
}

@Benchmark
public void fastmoney_static() {
    FAST_MONEY_BASE.subtract(FAST_MONEY_SUBSTRACT).multiply(3.4).divide(5.456);
    }
}

를 야기하는

Benchmark                                Mode  Cnt     Score    Error  Units
BigDecimalBenchmark.bigdecimal_string   thrpt   10   479.465 ± 26.821  ops/s
BigDecimalBenchmark.bigdecimal_valueOf  thrpt   10  1066.754 ± 40.997  ops/s
BigDecimalBenchmark.fastmoney           thrpt   10    83.917 ±  4.612  ops/s
BigDecimalBenchmark.fastmoney_static    thrpt   10   504.676 ± 21.642  ops/s
BigDecimalBenchmark.money               thrpt   10    59.897 ±  3.061  ops/s
BigDecimalBenchmark.money_static        thrpt   10   184.767 ±  7.017  ops/s

내가 빠진 것이 있으면 자유롭게 고쳐주세요


흥미롭게도 JDK9의 최신 항목으로 동일한 테스트를 실행합니다.
kensai

4

간단한 경우 (한 통화)면 충분합니다 Integer/ Long. 센트 (...) 또는 백분의 1 / 센트 (돈을 고정 분배기로 필요한 정밀도)로 유지하십시오


3

BigDecimal은 통화에 사용하기에 가장 적합한 데이터 유형입니다.

통화를위한 많은 컨테이너가 있지만 모두 기본 데이터 유형으로 BigDecimal을 사용합니다. BigDecimal을 잘못 사용하지 않으며 아마도 BigDecimal.ROUND_HALF_EVEN 반올림을 사용합니다.


2

나는 작은 유형을 사용하는 것을 좋아합니다이전 답변에서 제안한대로 double, BigDecimal 또는 int를 래핑하는 를 하는 것이 좋습니다. (정밀 문제가 발생하지 않으면 이중을 사용합니다).

타이니 타입은 타입 안전성을 제공하므로 다른 더블과 혼동하지 않아도됩니다.


6
나는 작은 유형을 좋아하지만 화폐 가치를 저장하기 위해 절대로 double을 사용 해서는 안됩니다 .
orien
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.