2012-02-28 6 views
32

У меня есть XSD, который требует от меня использовать BigDecimal для lat/lon. В настоящее время у меня есть lat/lon как двойники и конвертируйте их в BigDecimal, но мне нужно всего лишь использовать около 12 мест. Я не смог понять, как это установить. Кто-нибудь может мне с этим помочь?Установить конкретную точность BigDecimal

+0

Вы пробовали давать конструктор MathContext? – bdares

+1

Если я правильно вижу, вы говорите о десятичных разрядах, а не о точности с плавающей запятой. По крайней мере, принятый ответ приведет к 12 десятичным знакам, а не к 12 цифрам точности. – vbence

ответ

60

Вы можете использовать setScale(), например.

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

Duh. Оглядываясь назад на ошибку, которую я получил, он сказал, что округление было необходимо. Я только сделал .setScale (12). Как только я подтвержу это, я сделаю это как ответ. Благодарю. – Jason

+0

Я не знаю, почему он не просто использует метод округления по умолчанию, например, кастинг, я видел только ROUND_HALF_UP. Это довольно педантично. ;) –

+1

+1, так как это помогло мне с моей автоматизации сегодня :) – Brian

27

Название вопроса спрашивает о точности. BigDecimal различает масштаб и точность. Масштаб - это число десятичных знаков. Вы можете думать о точности как число significant figures, также известное как значимые цифры.

Некоторые примеры в Clojure.

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

(В Clojure, The M обозначает BigDecimal буквальным. Вы можете перевести Clojure на Java, если вам нравится, но я считаю, что это будет более компактным, чем Java!)

Вы можете легко увеличить масштаб:

(.setScale 0.00123M 7) ; 0.0M 

Но вы не можете снижения шкалы в точно так же:

(.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 

Таким образом, нет быстрого способа изменить точность. Я потратил время на борьбу с этим, написав этот вопрос и с проектом, над которым я работаю. Я считаю это, в лучшем случае, API CRAZYTOWN, а в худшем - ошибкой. Люди. Шутки в сторону?

Таким образом, лучшее, что я могу сказать, если вы хотите изменить точность, вам необходимо сделать следующие шаги:

  1. Lookup тока точности.
  2. Посмотрите текущий масштаб.
  3. Рассчитать изменение масштаба.
  4. Установите новую шкалу

Эти шаги, как и 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 не предоставляет это? Я что-то пропустил?

+0

Как насчет 'BigDecimal.round (новый MathContext (точность, RoundingModel.ROUND_HALF_EVEN))'? –

+1

@ PaŭloEbermann Вы спрашиваете или предлагаете? Ты это пробовал? Помогите поделиться подробными результатами для примеров, которые я пропустил в своем ответе? Если это так, я думаю, что это требует отдельного ответа. –

+1

Для любого, кто похож на меня и не знает немного Clojure, я думаю, что здесь показано решение: x.setScale (x.scale() + p - x.precision(), RoundingMode.HALF_UP) ', где' x' является 'BigDecimal', который вы хотите объединить, а' p' - количество значимых цифр, которые вы хотите сохранить. Он работает для меня, но поправьте меня, если я ошибаюсь! – Nateowami

3
BigDecimal decPrec = (BigDecimal)yo.get("Avg"); 
decPrec = decPrec.setScale(5, RoundingMode.CEILING); 
    String value= String.valueOf(decPrec); 

Таким образом, вы можете установить конкретную точность BigDecimal.

Здесь значение decPrec было 1,5726903423607562595809913132345426 , который округляется до 1.57267

0

Попробуйте этот код ...

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

Результат: 0,00000

Смежные вопросы