设置 BigDecimal 的特定精度

Posted

技术标签:

【中文标题】设置 BigDecimal 的特定精度【英文标题】:Set specific precision of a BigDecimal 【发布时间】:2012-02-28 13:35:58 【问题描述】:

我有一个XSD,它要求我使用 BigDecimal 作为纬度/经度。我目前将 lat/lon 作为双精度数,并将它们转换为 BigDecimal,但我只需要使用大约 12 个精度位。我一直无法弄清楚如何设置它。谁能帮我解决这个问题?

【问题讨论】:

你试过给构造函数一个 MathContext 吗? 如果我没看错,您说的是小数位,而不是浮点精度。至少接受的答案将导致 12 位小数,而不是 12 位精度。 【参考方案1】:

您可以使用setScale(),例如

double d = ...
BigDecimal db = new BigDecimal(d).setScale(12, BigDecimal.ROUND_HALF_UP);

【讨论】:

呃。回顾我得到的错误,它说四舍五入是必要的。我只做了.setScale(12)。一旦我确认它有效,我会按照回答进行。谢谢。 我不知道为什么它不像强制转换那样只使用默认的舍入方法,我只看到使用 ROUND_HALF_UP。它比较迂腐。 ;)【参考方案2】:

问题的标题询问精度。 BigDecimal 区分比例和精度。比例是小数位数。您可以将精度视为significant figures 的数量,也称为有效数字。

Clojure 中的一些示例。

(.scale     0.00123M) ; 5
(.precision 0.00123M) ; 3

(在 Clojure 中,M 指定一个 BigDecimal 文字。您可以根据需要将 Clojure 翻译成 Java,但我发现它比 Java 更紧凑!)

您可以轻松增加规模:

(.setScale 0.00123M 7) ; 0.0012300M

但你不能减小以完全相同的方式:

(.setScale 0.00123M 3) ; ArithmeticException Rounding necessary

您还需要通过舍入模式:

(.setScale 0.00123M 3 BigDecimal/ROUND_HALF_EVEN) ;
; Note: BigDecimal would prefer that you use the MathContext rounding
; constants, but I don't have them at my fingertips right now.

因此,更改比例很容易。但是精度呢?这并不像您希望的那么容易!

降低精度很容易:

(.round 3.14159M (java.math.MathContext. 3)) ; 3.14M

但是如何提高精度并不明显:

(.round 3.14159M (java.math.MathContext. 7)) ; 3.14159M (unexpected)

对于持怀疑态度的人,这只是不显示尾随零的问题:

(.precision (.round 3.14159M (java.math.MathContext. 7))) ; 6 
; (same as above, still unexpected)

FWIW,Clojure 对尾随零很小心,显示它们:

4.0000M ; 4.0000M
(.precision 4.0000M) ; 5

回到正轨...您可以尝试使用BigDecimal 构造函数,但它不会将精度设置为高于您指定的位数:

(BigDecimal. "3" (java.math.MathContext. 5)) ; 3M
(BigDecimal. "3.1" (java.math.MathContext. 5)) ; 3.1M

因此,没有快速更改精度的方法。我在写这个问题和我正在做的一个项目时花了很多时间来解决这个问题。我认为这充其量是一个 CRAZYTOWN API,最坏的情况是一个错误。人们。认真的吗?

所以,我可以说,如果您想更改精度,您需要执行以下步骤:

    查找当前精度。 查找当前比例。 计算比例变化。 设置新比例

这些步骤,作为 Clojure 代码:

(def x 0.000691M) ; the input number
(def p' 1) ; desired precision
(def s' (+ (.scale x) p' (- (.precision x)))) ; desired new scale
(.setScale x s' BigDecimal/ROUND_HALF_EVEN)
; 0.0007M

我知道,这就是为了改变精度的很多步骤!

为什么 BigDecimal 还没有提供这个?我是不是忽略了什么?

【讨论】:

BigDecimal.round(new MathContext(precision, RoundingModel.ROUND_HALF_EVEN)) 呢? @PaŭloEbermann 你是在问还是建议?你试过了吗?关心分享我在答案中运行的示例的详细结果吗?如果是这样,我认为它需要一个单独的答案。 对于像我一样并且对 Clojure 不太了解的任何人,我认为这里显示的解决方案是x.setScale(x.scale() + p - x.precision(), RoundingMode.HALF_UP),其中x 是您要舍入的BigDecimal,而@ 987654339@ 是您要保留的有效数字的数量。它对我有用,但如果我错了,请纠正我! @Nateowami 我喜欢你的解决方案,但不幸的是它在极端情况下似乎无法正常工作,例如为 99.99。您的声明(3 个有效数字)将其变为 100.0,而它应该只是 100。有 1 个额外的有效数字,因为 99 到 100 的“溢出” @Kr1z 好点。猜测这是这个答案的限制。我唯一能想到的就是事后检查并再次四舍五入,如果位数错误。我认为它不可能第二次添加数字。【参考方案3】:
 BigDecimal decPrec = (BigDecimal)yo.get("Avg");
 decPrec = decPrec.setScale(5, RoundingMode.CEILING);
 String value= String.valueOf(decPrec);

这样您可以设置BigDecimal 的特定精度。

decPrec 的值为1.5726903423607562595809913132345426 四舍五入为1.57267

【讨论】:

【参考方案4】:

试试这个代码...

    Integer perc = 5;
    BigDecimal spread = BigDecimal.ZERO; 
    BigDecimal perc = spread.setScale(perc,BigDecimal.ROUND_HALF_UP);
    System.out.println(perc);

结果:0.00000

【讨论】:

以上是关于设置 BigDecimal 的特定精度的主要内容,如果未能解决你的问题,请参考以下文章

BigDecimal大小判断

double和float进行小数计算精度不够

将 double 转换为 BigDecimal 并设置 BigDecimal 精度

Object转为Bigdecimal

[类型转换] Object 转换为 BigDecimal

具有智能精度(比例)的 BigDecimal [重复]