使用双精度值时出现困惑,当groovy决定将值制成double还是BigDecimal?

5

我有些困惑如何使用double类型的值。

当我按照以下方式使用时,会出现以下问题:

double foo = 20.46455
assert 20 == foo.round()
assert 20.46 == foo.round(2)

它正常工作,但当我使用类似以下代码时出现问题:

def foo = 20.46455
assert 20 == foo.round()

它抛出了以下异常:

java.lang.NullPointerException

以及

def foo = 20.46455
assert 20.46 == foo.round(2)

出现以下异常信息:

groovy.lang.MissingMethodException: No signature of method: java.math.BigDecimal.round() is applicable for argument types: (java.lang.Integer) values: [2] Possible solutions: round(java.math.MathContext), find(), pow(int), power(java.lang.Integer), find(groovy.lang.Closure), and(java.lang.Number)

这说明在默认情况下,groovy会将值保存在BigDecimal中,并且BigDecimal.round()需要java.math.MathContext作为输入。

但当我使用Math.round()时,它要求输入double,那么为什么下面的语句会通过,而groovy默认会将其保留在BigDecimal

def foo = 20.46455
assert 20 == Math.round(foo)

为什么我需要使用 .toDouble() 来通过测试用例,而 foo 的值已经是以下的 double 格式了呢?

def foo = 20.46455
assert 20 == foo.toDouble().round()
assert 20.46 == foo.toDouble().round(2)

注意:我不想知道如何对双精度值进行四舍五入,我只想知道为什么groovy在每种情况下的行为不同?


@yash 我不想四舍五入数字,我只是想知道为什么 groovy 在每种情况下的行为不同。谢谢..:) - Saurabh Gaur
@NathanHughes 实际上我知道如何四舍五入 BigDecimalDouble。是的,你说得对,我的问题是“当Groovy决定将某些东西作为double而不是BigDecimal时?” - Saurabh Gaur
我也更新了问题的标题,谢谢.. :) - Saurabh Gaur
不要忘记添加缺失的数据类型,而是让Groovy假设!对我很有用! - Gaurav
1个回答

11

Groovy会自动隐式地使用BigDecimal处理任何浮点数,除非您定义了类型或者为数字添加后缀(如D)。

例如:

def foo = 20.46455
println foo.getClass()

输出:

java.math.BigDecimal类

double foo = 20.45645
println foo.getClass()

输出结果:

java.lang.Double 类

def foo = 20.45645d
println foo.getClass()

输出:

class java.lang.Double

类型转换:

Groovy也有一些自动类型转换,这就是为什么即使Math.round()只接受doublefloat原始类型作为参数,当您传递一个BigDecimal时代码不会失败的原因。为了证明这一点,您可以实现自己的round函数并检查类型转换的情况:

示例:

def round(double foo) {
   println foo.getClass()
   return foo.round()
}

def foo = 20.46455
println foo.getClass()
assert 20 == round(foo)

输出:

java.math.BigDecimal类

java.lang.Double类

另外一些隐式转换的有效示例:

def round(float foo) {
   println foo.getClass()
   return foo.round()
}

def foo = 20.46455
println foo.getClass()
assert 20 == round(foo)

输出:

java.math.BigDecimal 类

java.lang.Float 类

def round(float foo) {
   println foo.getClass()
   return foo.round()
}

def foo = 20
println foo.getClass()
assert 20 == round(foo)

输出:

class java.lang.Integer

class java.lang.Float

def round(double foo) {
   println foo.getClass()
   return foo.round()
}

def foo = 20
println foo.getClass()
assert 20 == round(foo)

输出:

类 java.lang.Integer

类 java.lang.Double

def round(BigDecimal foo) {   
   println foo.getClass()
   return foo
}

double foo = 20.0
println foo.getClass()
assert 20 == round(foo)

输出:

class java.lang.Double

class java.lang.BigDecimal

通常来说,如果数字是基于浮点数的(double, float, BigDecimal),它们之间将会隐式进行类型转换,如果尝试将它们转换为非浮点数(如intlong),代码将抛出异常。如果一个数字不是浮点数类型(int, long),它可以在非浮点数和浮点数类型之间进行转换,因为浮点数也包括非浮点数作为子集 (例如,1 可以表示为 1.0)。这是有道理的,因为你不能从一个 float 变量传递浮点信息到一个 int (20.5 无法用一个 int 变量表示),但在大多数情况下,你可以反过来做,只有在对于大值时的溢出有时候需要特殊处理(例如,将一个非常大的 long 数字转换为一个 float 变量)。


那为什么def foo = 20.46455;assert 20 == Math.round(foo)会通过呢? - Saurabh Gaur
def foo = 20.46455 让 Groovy 使用类型推断来决定变量的类型,它选择了 BigDecimal 类型。double foo = 20.46455 强制变量成为 double 类型。你也可以使用后缀来强制声明 double 类型,像这样 def foo = 20.46455D - Jose Ignacio Acin Pozo
但它在不强制转换为“double”的情况下通过了,这就是我感到困惑的原因,我只使用了“double foo = 20.46455”,当我将其用作“assert 20 == Math.round(foo)”时,Groovy会自动将其视为“double”,但是当我将其用作“foo.round()”时,Groovy将其视为“BigDecimal”,并抛出异常... - Saurabh Gaur
1
抱歉,我现在明白你的意思了。Groovy也具有自动类型转换的功能。让我扩展我的答案来包含这一点。 - Jose Ignacio Acin Pozo
1
如果对象本身是BigDecimal def foo = 20.46455,并且您尝试调用一个未为BigDecimal实现的方法(如round()),它将抛出异常。但是,如果您将其作为参数传递,Groovy将尝试适应该类型(例如将BigDecimal传递给期望double或float参数的方法),但如果转换不可能仍会抛出异常。我将进一步扩展我的答案以显示更多隐式转换。 - Jose Ignacio Acin Pozo
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接