繁体   English   中英

BigDecimal - 使用 new 或 valueOf

[英]BigDecimal - to use new or valueOf

我遇到了两种从双 d 中获取 BigDecimal object 的方法。

  1. new BigDecimal(d)
  2. BigDecimal.valueOf(d)

哪种方法更好? valueOf会创建一个新的 object 吗?

一般来说(不仅仅是 BigDecimal),推荐什么 - new 或 valueOf?

这是两个不同的问题:“我应该为BigDecimal使用什么?” 和“我一般做什么?”

对于BigDecimal :这有点棘手,因为他们没有做同样的事情 BigDecimal.valueOf(double)将使用传入的double值的规范String表示来实例化BigDecimal对象。 换句话说: BigDecimal对象的值将是您在执行System.out.println(d)时看到的值。

但是,如果使用new BigDecimal(d) ,则BigDecimal将尝试尽可能准确地表示double值。 通常会导致存储的数字比您想要的多得多。 严格来说,它比valueOf()更正确,但它更不直观。

在JavaDoc中有一个很好的解释:

这个构造函数的结果可能有点不可预测。 有人可能会假设在Java中编写new BigDecimal(0.1)会创建一个BigDecimal ,它恰好等于0.1(未缩放值为1,标度为1),但它实际上等于0.1000000000000000055511151231257827021181583404541015625。 这是因为0.1不能精确地表示为double (或者,就此而言,作为任何有限长度的二进制分数)。 因此,传递给构造函数的值并不完全等于0.1,尽管有外观。

通常, 如果结果相同 (即不是BigDecimal的情况,但在大多数其他情况下),则valueOf()应该是首选:它可以缓存常见值(如Integer.valueOf() )它甚至可以在不需要更改调用者的情况下更改缓存行为。 new始终实例化一个新值,即使没有必要(最好的例子: new Boolean(true) vs. Boolean.valueOf(true) )。

如果您使用的是BigDecimal对象来存储货币值,那么我强烈建议你不要在他们的计算涉及到的任何地方任何双重价值。

正如另一个答案所述,双重值存在已知的准确性问题,这些问题会让您大吃一惊。

一旦你过了那个问题,答案就很简单了。 始终使用带有String值的构造函数方法作为构造函数的参数,因为String没有valueOf方法。

如果您需要证明,请尝试以下操作:

BigDecimal bd1 = new BigDecimal(0.01);
BigDecimal bd2 = new BigDecimal("0.01");
System.out.println("bd1 = " + bd1);
System.out.println("bd2 = " + bd2);

您将获得以下输出:

bd1 = 0.01000000000000000020816681711721685132943093776702880859375
bd2 = 0.01

另请参阅此相关问题

基本上valueOf(double val)就是这样做的:

return new BigDecimal(Double.toString(val));

因此 - >是的,将创建一个新对象:)。

一般来说,我认为这取决于你的编码风格。 如果两者都是相同的结果,我不会混合valueOf和“new”。

刚刚意识到,我在这里赞成所有答案,并写了我自己的;-)

BigDecimal - 需要一个精确的表示

如果您使用BigDecimal ,这意味着您需要0.1和其他 10 的负幂的精确表示(通常您会处理金钱)。

Double 表示麻烦(涉及 BigDecimal)

然后,如果您发现自己必须使用BigDecimal操作double精度(或浮点)值,那么您将遇到双重麻烦,因为不可能将0.1表示为以 2 为底的双精度值。机器“存储”双精度值( IEEE-754浮点算术的标准)作为基数 2,如果您有兴趣,这里是一个很好的文章。)无论您使用的编程语言如何。 邓肯的回答说明了我想说的。

例子

考虑这个例子,使用一个双精度数和一个双精度数:(还请注意,还有computerized scientific notation部分需要处理,更多信息在这里)。

 public static void main(String[] args) {
    System.out.println("..........TINY DOUBLE: d1 = 0.0001");
    Double d1 = 0.0001;<br>
    System.out.println("Double number:               " + d1);        
    System.out.println("new BigDecimal(d1):          " + new BigDecimal(d1));       
    System.out.println("new BigDecimal(d1.toString():" + new BigDecimal(d1.toString()));         
    System.out.println("BigDecimal.valueOf(d1):      " + BigDecimal.valueOf(d1)); //Using Double.toString(d1) under the hood        
    System.out.println("new BigDecimal(\"0.0001\"):    " + new BigDecimal("0.0001"));
    System.out.println();       
    System.out.println();
    System.out.println("..........HUGE DOUBLE: d2 = 99999999999999999999.1234");        
    Double d2 = 99999999999999999999.1234;      
    System.out.println("Double number:                                " + d2);      
    System.out.println("new BigDecimal(d2.toString()):                " + new BigDecimal(d2.toString()));       
    System.out.println("new BigDecimal(d2):                           " + new BigDecimal(d2));      
    System.out.println("BigDecimal.valueOf(d2):                       " + BigDecimal.valueOf(d2));      
    System.out.println("new BigDecimal(\"99999999999999999999.1234\"):  " + new BigDecimal("99999999999999999999.1234"));   
}

结果 - 仔细分析

..........小双:d1 = 0.0001
双数:1.0E-4
新的大十进制(d1):0.000100000000000000004792173602385929598312941379845142364501953125
新的 BigDecimal(d1.toString():0.00010
BigDecimal.valueOf(d1): 0.00010
新的 BigDecimal("0.0001"): 0.0001

..........巨大的双倍:d2 = 99999999999999999999.1234
双数:1.0E20
新 BigDecimal(d2.toString()): 1.0E+20
新的大十进制(d2):100000000000000000000
BigDecimal.valueOf(d2): 1.0E+20
新的 BigDecimal("99999999999999999999.1234"): 99999999999999999999.1234

奖金 - 有效 Java 第 3 版(约书亚布洛赫)

第 60 条:如果需要准确的答案,请避免使用 float 或 double

float 和 double 类型特别不适合货币计算,因为不可能将 0.1(或任何其他 10 的负幂)精确地表示为 float 或 double。

然而,使用 BigDecimal 有两个缺点:它比使用原始算术类型要方便得多,而且速度要慢得多。 如果您要解决一个简短的问题,则后一个缺点无关紧要,但前者可能会惹恼您。

使用 BigDecimal 的替代方法是使用 int 或 long,具体取决于所涉及的数量,并自己跟踪小数点。 在此示例中,显而易见的方法是以美分进行所有计算。

数学倾向的额外阅读;-)

每个计算机科学家都应该知道的关于浮点运算的知识

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM