Bir BigDecimal'ın tam olarak float veya double'a dönüşüp dönüşmediğini nasıl anlayabilirim?


10

Sınıfın BigDecimalkayıpsız dönüşümü garanti etmek için bazı yararlı yöntemleri vardır:

  • byteValueExact()
  • shortValueExact()
  • intValueExact()
  • longValueExact()

Ancak yöntemler floatValueExact()ve doubleValueExact()mevcut değildir.

Ben yöntemleri için OpenJDK kaynak kodunu okumak floatValue()ve doubleValue(). Her ikisi de sırasıyla pozitif veya negatif sonsuzluğa geri dönebilecek Float.parseFloat()ve Double.parseDouble()sırasıyla geri dönmektedir. Örneğin, 10.000 9'luk bir dizeyi ayrıştırmak pozitif sonsuzluk döndürür. Anladığım kadarıyla BigDecimaldahili bir sonsuzluk kavramı yok. Ayrıca, 100 9'luk bir dizeyi sonsuz olarak değil, kesinlik kaybeden doubleverir 1.0E100.

Makul bir uygulama nedir floatValueExact()ve doubleValueExact()?

Ben düşündüm doublebirleştirerek çözümü BigDecimal.doubleValue(), BigDecial.toString(), Double.parseDouble(String)ve Double.toString(double), ama pis bir görünüyor. Burada sormak istiyorum çünkü daha basit bir çözüm olabilir (zorunlu!).

Açık olmak gerekirse, yüksek performanslı bir çözüme ihtiyacım yok.


7
Sanırım ikiye dönüştürebilirsin, sonra ikiliyi BigDecimal'e dönüştürebilirsin ve aynı değeri alıp almadığına bakabilirsin. Kullanım durumunun ne olduğundan emin değilim. Çiftler kullanırsanız, kesin olmayan değerlere zaten sahip olduğunuzu kabul etmiş olursunuz. Kesin değerler istiyorsanız, BigDecimal ile kalın.
JB Nizet

Yanıtlar:


6

Gönderen okuma dokümanlar , bütün onunla yapar numTypeValueExact varyantlar bir kısmını kısmının varlığını denetlemek için veya değer sayısal tür için çok büyük ve eğer istisna atar.

Gelince floatValue()ve doubleValue()benzer taşma denetimi yerine döndürür, ancak bunun yerine bir istisna atma yapılıyor Double.POSITIVE_INFINITYveya Double.NEGATIVE_INFINITYçiftler ve Float.POSITIVE_INFINITYya Float.NEGATIVE_INFINITYyüzen için.

Bu nedenle en makul (ve en basit) uygulaması exactşamandıra ve çift yöntemleri, sadece dönüşüm getiri olmadığını kontrol etmelidir POSITIVE_INFINITYya NEGATIVE_INFINITY.


Dahası , bunu unutma BigDecimalkullanarak gelen hassas eksikliği işlemek için tasarlanmış floatveya doublebu nedenle gibi büyük irrasyonellerde için @JB Nizet yorumladı , yukarıdaki ekleyebileceğiniz başka bir kontrol dönüştürmek olacağını doubleveya floatgeri BigDecimalhala almak olmadığını görmek için aynı değer. Bu, dönüşümün doğru olduğunu kanıtlamalıdır.

İşte böyle bir yöntem neye benzeyecektir floatValueExact():

public static float floatValueExact(BigDecimal decimal) {
    float result = decimal.floatValue();
    if (!Float.isInfinite(result)) {
        if (new BigDecimal(String.valueOf(result)).compareTo(decimal) == 0) {
            return result;
        }
    }
    throw new ArithmeticException(String.format("%s: Cannot be represented as float", decimal));
}

Yukarıdakilerin compareToyerine kullanımı equals, çeklere karşı çok katı hale gelmemek için kasıtlıdır. equalsyalnızca iki BigDecimalnesne aynı değere ve ölçeğe (ondalık kesir kısmının boyutu) sahip olduğunda doğru olarak değerlendirilir , oysa compareTobu fark önemli olmadığında bu farkı göz ardı eder. Örneğin 2.0vs 2.00.


Çok kurnaz. Tam olarak ihtiyacım olan şey! Not: Float.isFinite()veya seçeneğini de kullanmak isteyebilirsiniz Float.isInfinite(), ancak bu isteğe bağlıdır. :)
kevinarpe

3
BigDecimal'daki equals () değeri 2.0 ve 2.00'ü farklı değerler olarak değerlendireceğinden, CompareTo over eşitlerini de tercih etmelisiniz.
JB Nizet

Şamandıra olarak tam olarak temsil edilemeyen değerler çalışmaz. Örnek: 123.456f. Sanırım bunun nedeni 32-bit float ile 64-bit çift arasındaki farkın anlamlı boyutu (mantis). Senin yukarıdaki kodda, daha iyi sonuçlar elde: if (new BigDecimal(result, MathContext.DECIMAL32).equals(decimal)) {. Bu makul bir değişiklik mi ... yoksa kayan nokta değerlerinin başka bir köşe durumunu mu kaçırıyorum?
kevinarpe

1
@kevinarpe - 123.456fkavramsal olarak 1.0E100 örneğinizle aynıdır. BigDecimal -> ikili kayan noktadan dönüşümün kesin olup olmadığını kontrol etmeniz gerekiyorsa , o zaman sorunun ihtiyaç olduğunu; yani dönüşüm düşünülmemelidir.
Stephen C
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.