Java应用程序中用于赚钱的最佳数据类型是什么?

que*_*orn 169 java types currency

Java应用程序中用于资金的最佳数据类型是什么?

Buh*_*ndi 123

Java具有Currency代表ISO 4217货币代码的类. BigDecimal是表示货币十进制值的最佳类型.

Joda Money提供了一个代表金钱的图书馆.

  • "如果我每次看到有人使用FLOAT来储存货币时都有一毛钱,我就会有999.997634美元" - Bill Karwin (26认同)
  • @Borat Sagdiyev [这就是为什么](http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems).另外,你可以参考[this](https://www.securecoding.cert.org/confluence/display/java/NUM04-J.+Do+not+use+floating-point+numbers+if+precise+computation + +是必需的). (16认同)
  • 为什么我们不能使用float或double? (4认同)
  • @Borat:如果您知道自己在做什么,请参阅Peter Lawrey撰写的[本文](http://vanillajava.blogspot.com/2011/08/double-your-money-again.html).但是,使用BigDecimals进行所有四舍五入似乎至少是一件很麻烦的事. (2认同)

Abd*_*ull 35

您可以使用Money和Currency API(JSR 354).此API有望成为Java 9的一部分.只要向项目添加适当的依赖项,就可以在Java 7和Java 8中使用此API.

对于Java 8,将以下引用实现添加为您的依赖项pom.xml:

<dependency>
    <groupId>org.javamoney</groupId>
    <artifactId>moneta</artifactId>
    <version>1.0</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)

此依赖项将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") );
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 您是否有Oracle决定不在Java 9中包含Java Money的来源? (3认同)
  • 序列化并保存到数据库怎么样?通过有线发送应使用什么格式? (2认同)

rat*_*eak 23

表示可能的最小值的整数类型.换句话说,你的程序应该用美分/欧元来思考.

这不应该阻止你把gui翻译成美元/欧元.

  • @eversor需要超过2000万美元的大多数应用程序如果做了很长时间就不需要那么多就足够了,因为即使我们的政府处理的东西也不足以满足要求. (5认同)
  • 许多银行每天处理的金额远远超过20,000,000美元.这甚至没有考虑到日元等对美元汇率很高的货币.整数类型可能最好避免舍入问题,尽管它们会对利息和汇率计算产生混乱.但是,根据应用程序的不同,您可能需要64位整数类型. (4认同)
  • @ratchetfreak可能更好地使用很长时间. (3认同)

Phi*_*ons 11

可以使用BigDecimal,在这里可以看到为什么不使用Float或Double的很好解释:为什么不使用Double或Float来表示货币?


Vir*_*ual 9

JSR 354:货币和货币API

JSR 354提供了一个API,用于使用货币和货币表示,传输和执行综合计算.您可以从以下链接下载:

JSR 354:货币和货币API下载

该规范包括以下内容:

  1. 用于处理货币金额和货币的API
  2. 用于支持可互换实现的API
  3. 用于创建实现类实例的工厂
  4. 用于计算,转换和格式化货币金额的功能
  5. 用于处理货币和货币的Java API,计划包含在Java 9中.
  6. 所有规范类和接口都位于javax.money.*包中.

JSR 354示例:Money和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));
Run Code Online (Sandbox Code Playgroud)

使用参考实现API时,必要的代码更简单:

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

API还支持使用MonetaryAmounts进行计算:

MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmount otherMonetaryAmount = monetaryAmount.divide(2).add(Money.of(5, "EUR"));
Run Code Online (Sandbox Code Playgroud)

CurrencyUnit和MonetaryAmount

// getting CurrencyUnits by locale
CurrencyUnit yen = MonetaryCurrencies.getCurrency(Locale.JAPAN);
CurrencyUnit canadianDollar = MonetaryCurrencies.getCurrency(Locale.CANADA);
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

可以使用舍入运算符舍入MonetaryAmounts:

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
Run Code Online (Sandbox Code Playgroud)

使用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"));
Run Code Online (Sandbox Code Playgroud)

自定义MonetaryAmount操作

// 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
Run Code Online (Sandbox Code Playgroud)

资源:

使用JSR 354处理Java中的货币和货币

研究Java 9货币和货币API(JSR 354)

另见:JSR 354 - 货币和货币


Liv*_* T. 6

我会用Joda Money

它仍处于0.6版本,但看起来非常有前景


San*_*hak 6

您应该使用BigDecimal 来表示货币值。它允许您使用各种舍入模式,在金融应用中,舍入模式通常是一个硬要求,甚至可能是法律规定的。


Fed*_*ani 6

我已经做了一个微基准测试(JMH)来比较Moneta(java货币JSR 354实现)与BigDecimal的性能.

令人惊讶的是,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);
    }
}
Run Code Online (Sandbox Code Playgroud)

导致

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
Run Code Online (Sandbox Code Playgroud)

如果我错过了什么,请随时纠正我


GKi*_*lin 5

对于简单的情况(一种货币),它就足够了int/ long。以美分(...)或百分之一/千分之一(固定分频器所需的任何精度)为单位