Java 使用BigDecimal处理货币

Java 使用BigDecimal处理货币,java,currency,bigdecimal,Java,Currency,Bigdecimal,我试图使用long为货币创建自己的类,但显然我应该使用BigDecimal。有人能帮我开始吗?对于美元货币,使用BigDecimals的最佳方法是什么,例如,将其设置为至少但不超过小数点后2位,等等。BigDecimal的API非常庞大,我不知道使用哪种方法。另外,BigDecimal具有更好的精度,但如果它通过一个double,这些不是都丢失了吗?如果我使用newBigDecimal(24.99),它与使用double有什么不同?或者我应该使用使用字符串的构造函数吗?1)如果您限于双精度精度,

我试图使用long为货币创建自己的类,但显然我应该使用
BigDecimal
。有人能帮我开始吗?对于美元货币,使用
BigDecimal
s的最佳方法是什么,例如,将其设置为至少但不超过小数点后2位,等等。
BigDecimal
的API非常庞大,我不知道使用哪种方法。另外,
BigDecimal
具有更好的精度,但如果它通过一个
double
,这些不是都丢失了吗?如果我使用new
BigDecimal(24.99)
,它与使用
double
有什么不同?或者我应该使用使用
字符串
的构造函数吗?

1)如果您限于
双精度
精度,使用
bigdecime
s的一个原因是使用从
double
s创建的
bigdecime
s实现操作

2)
BigDecimal
由一个任意精度的整数无标度值和一个非负32位整数标度组成,而double则将原语类型的值
double
包装在一个对象中。
Double
类型的对象包含一个类型为
Double

3) 这应该没什么区别

您应该不会对$和精度有任何困难。一种方法是使用
System.out.printf

以下是一些提示:

  • 如果您需要它提供的精度,请使用
    BigDecimal
    进行计算(货币价值通常需要这样做)
  • 使用该类进行显示。本课程将处理不同货币金额的本地化问题。然而,它只接受原语;因此,如果您可以接受由于转换为
    double
    而导致的精度上的微小变化,那么您可以使用这个类
  • 使用
    NumberFormat
    类时,请在
    BigDecimal
    实例上使用
    scale()
    方法来设置精度和舍入方法
  • 附:如果你想知道

    缴费灵:

    创建
    BigDecimal
    实例

    这相当简单,因为,和
    String
    对象。你可以用那些。比如说,

    BigDecimal modelVal = new BigDecimal("24.455");
    BigDecimal displayVal = modelVal.setScale(2, RoundingMode.HALF_EVEN);
    
    显示
    BigDecimal
    实例

    您可以使用
    setMinimumFractionDigits
    setMaximumFractionDigits
    方法调用来限制显示的数据量

    NumberFormat usdCostFormat = NumberFormat.getCurrencyInstance(Locale.US);
    usdCostFormat.setMinimumFractionDigits( 1 );
    usdCostFormat.setMaximumFractionDigits( 2 );
    System.out.println( usdCostFormat.format(displayVal.doubleValue()) );
    

    如果要将小数点后两位四舍五入为美分,请使用
    BigDecimal.setScale(2,BigDecimal.ROUND_HALF_UP)
    。但在进行计算时要注意舍入误差。在进行货币价值四舍五入时,您需要保持一致。在所有计算完成后,在末尾进行一次右舍入,或者在进行任何计算之前对每个值应用舍入。使用哪一种取决于您的业务需求,但总体而言,我认为在末尾进行四舍五入似乎对我更有意义


    为货币值构造
    BigDecimal
    时,请使用
    字符串。如果使用双精度
    ,它的末尾将有一个尾随浮点值。这是由于计算机体系结构有关如何以二进制格式表示
    double
    /
    float
    值的问题。

    在javapractices.com上有一个关于如何实现这一点的广泛示例。请特别参阅该类,该类旨在使货币计算比直接使用
    BigDecimal
    更简单

    这个
    Money
    类的设计旨在使表达式更自然。例如:

    if ( amount.lt(hundred) ) {
     cost = amount.times(price); 
    }
    

    WEB4J工具有一个类似的类,名为,它比
    Money
    类更精巧。

    我建议对Money模式进行一些研究。马丁·福勒(Martin Fowler)在他的《分析模式》一书中对此进行了更详细的阐述

    public class Money {
    
        private static final Currency USD = Currency.getInstance("USD");
        private static final RoundingMode DEFAULT_ROUNDING = RoundingMode.HALF_EVEN;
    
        private final BigDecimal amount;
        private final Currency currency;   
    
        public static Money dollars(BigDecimal amount) {
            return new Money(amount, USD);
        }
    
        Money(BigDecimal amount, Currency currency) {
            this(amount, currency, DEFAULT_ROUNDING);
        }
    
        Money(BigDecimal amount, Currency currency, RoundingMode rounding) {
            this.currency = currency;      
            this.amount = amount.setScale(currency.getDefaultFractionDigits(), rounding);
        }
    
        public BigDecimal getAmount() {
            return amount;
        }
    
        public Currency getCurrency() {
            return currency;
        }
    
        @Override
        public String toString() {
            return getCurrency().getSymbol() + " " + getAmount();
        }
    
        public String toString(Locale locale) {
            return getCurrency().getSymbol(locale) + " " + getAmount();
        }   
    }
    
    关于用法:

    您可以使用
    Money
    对象来表示所有货币,而不是
    BigDecimal
    。将货币表示为大小数将意味着您将有权在显示货币的每个位置格式化货币。想象一下,如果显示标准发生变化。你必须到处编辑。您可以使用
    Money
    模式将货币的格式集中到一个位置

    Money price = Money.dollars(38.28);
    System.out.println(price);
    

    或者,等待。Java货币和货币API即将推出

    基本数字类型对于在内存中存储单个值很有用。但是当处理使用double和float类型的计算时,舍入会出现问题。这是因为内存表示不能精确映射到值。例如,一个双精度值应该是64位的,但Java并不使用所有的64位,它只存储它认为数字中重要的部分。因此,当您将float或double类型的值相加时,可能会得出错误的值


    请看一个简短的剪辑

    我会很激进。无大小数。

    这是一篇很棒的文章

    来自这里的想法

    import java.math.BigDecimal;
    
    public class Main {
    
        public static void main(String[] args) {
            testConstructors();
            testEqualsAndCompare();
            testArithmetic();
        }
    
        private static void testEqualsAndCompare() {
            final BigDecimal zero = new BigDecimal("0.0");
            final BigDecimal zerozero = new BigDecimal("0.00");
    
            boolean zerosAreEqual = zero.equals(zerozero);
            boolean zerosAreEqual2 = zerozero.equals(zero);
    
            System.out.println("zerosAreEqual: " + zerosAreEqual + " " + zerosAreEqual2);
    
            int zerosCompare = zero.compareTo(zerozero);
            int zerosCompare2 = zerozero.compareTo(zero);
            System.out.println("zerosCompare: " + zerosCompare + " " + zerosCompare2);
        }
    
        private static void testArithmetic() {
            try {
                BigDecimal value = new BigDecimal(1);
                value = value.divide(new BigDecimal(3));
                System.out.println(value);
            } catch (ArithmeticException e) {
                System.out.println("Failed to devide. " + e.getMessage());
            }
        }
    
        private static void testConstructors() {
            double doubleValue = 35.7;
            BigDecimal fromDouble = new BigDecimal(doubleValue);
            BigDecimal fromString = new BigDecimal("35.7");
    
            boolean decimalsEqual = fromDouble.equals(fromString);
            boolean decimalsEqual2 = fromString.equals(fromDouble);
    
            System.out.println("From double: " + fromDouble);
            System.out.println("decimalsEqual: " + decimalsEqual + " " + decimalsEqual2);
        }
    }
    
    它打印

    From double: 35.7000000000000028421709430404007434844970703125
    decimalsEqual: false false
    zerosAreEqual: false false
    zerosCompare: 0 0
    Failed to devide. Non-terminating decimal expansion; no exact representable decimal result.
    
    将BigDecimal存储到数据库中如何?见鬼,它还存储为双倍值???至少,如果我在没有任何高级配置的情况下使用mongoDb,它会将
    BigDecimal.TEN
    存储为
    1E1

    可能的解决方案? 我附带了一个使用字符串,将Java中的BigDecimal作为字符串存储到数据库中。您有验证,例如
    @NotNull
    @Min(10)
    ,等等。。。然后,您可以在更新或保存时使用触发器来检查当前字符串是否是您需要的数字。但是mongo没有触发器。

    我有一个缺点,就是玩得很开心-

    我需要生成swagger,以便我们的前端团队理解我将一个数字作为字符串传递给他们DateTime例如,表示为字符串

    还有一个
    From double: 35.7000000000000028421709430404007434844970703125
    decimalsEqual: false false
    zerosAreEqual: false false
    zerosCompare: 0 0
    Failed to devide. Non-terminating decimal expansion; no exact representable decimal result.