为什么bigDecimal的scale()方法与stripTrailingZeros()方法结合时返回负值?

Naw*_*sta 6 java android bigdecimal

为了避免出现像 10.0000 这样的值BigDecimal,我使用了stripTrailingZeros()返回值 10 而不是预期的 10.0000 的方法。现在,10 的小数位数应该为 0,因为对于零或正值,小数位数是小数点右侧的位数。然而,情况并非如此,因为它返回 -1。

Log.d("click", BigDecimal("10").toPlainString()) // return 10
Log.d("click", BigDecimal("10").stripTrailingZeros().toPlainString()) // return 10
Run Code Online (Sandbox Code Playgroud)

上面的代码返回相同的值 10 并且stripTrailingZeros()对其值没有影响。

Log.d("click", BigDecimal("10").scale().toString())  // return 0
Log.d("click", BigDecimal("10").stripTrailingZeros().scale().toString()) // return -1
Run Code Online (Sandbox Code Playgroud)

但这些值的范围不同,因为它返回 0 和 -1,而且

Log.d("click", BigDecimal("100").stripTrailingZeros().scale().toString()) // return -2
Log.d("click", BigDecimal("1000").stripTrailingZeros().scale().toString()) // return -3
Run Code Online (Sandbox Code Playgroud)

有什么办法可以得到我预期的结果吗?谁能解释为什么scale()方法返回负值?

Rud*_*uis 6

这是由 BigDecimal 的存储方式决定的。它使用刻度来记住小数位数。

unscaled值和规模

Bigdecimals 可以记住它的小数位数(小数点分隔符后面的位数)。为此,它们被存储为两个值:

  • 一个unscaledValue(通常是 BigInteger,但对于小值,这也可能很长),其中包含(有效)数字和
  • 比例尺(将unscaledValue乘以 10 的负幂获得标称,即一种负指数)。

示例:值1.0存储为 UnscaledValue(我将其称为 UV)10 和比例 1 ( 10 x 10 -1 )。值3.000存储为 UV 3000 和比例 3 ( 3000 x 10 -3 ),12.34存储为 UV=1234 和比例=2(即1234 x 10 -2),依此类推。

您的值10存储为 UV=10 且比例=0(简写:UV=10,s=0)。但是,如果您去掉尾随零,则转换为例如1.000 (UV=1000,S=3),您从 UV 中去掉试验零并相应地调整比例,因此它变为 (UV=1,S=0):UV 丢失3 个零,刻度调整为 0。

也可以使用负标度来表示 10 的()幂。值2e20存储为 UV=2, s=-20 ( 2 x 10 20 ),而不是存储为 UV=200000000000000000000, s=0。这节省了很多空间。同样,2.00e20存储为 UV=200、s=-18 或200 x 10 18

这就是为什么当删除尾随零时,10变为 UV=1, s=-1 ( 1 x 10 1 ),而不是 UV=10, s=0 ( 10 x 10 0 )。6000 (UV=6000, s=0) 变为6E+3 (UV=6, s =-3; 6 x 10 3 )。

设置比例

但您可以轻松地将刻度设置回 0,而无需更改标称值。如果比例为负,则不会发生舍入。

BigDecimal b1 = new BigDecimal("10.000"); // UV=10000, s=3, 10000 x 10**-3
System.out.println(b1);
b1 = b1.stripTrailingZeros();             // UV=1, s=-1, *all* trailing zeros removed,
                                          // as desired: 10000 --> 1
System.out.println(b1);
b1 = b1.setScale(0);                      // UV=10, s=0
System.out.println(b1);
Run Code Online (Sandbox Code Playgroud)

输出:

10.000
1E+1
10
Run Code Online (Sandbox Code Playgroud)

因此,要仅去除尾随小数零,请执行以下操作:

10.000
1E+1
10
Run Code Online (Sandbox Code Playgroud)