2010-07-14 5 views
6

पाने के लिए मैं थोड़ा इस ArithmeticException से उलझन में हूँ। मैंने स्कैला 2.8.0.आरसी 6 और आरसी 7 पर यह कोशिश की है।RichDouble.to आपरेशन का उपयोग करते हुए एक NumericRange

scala> 7.12 to(8, 0.2) 
res0: scala.collection.immutable.NumericRange[Double] = NumericRange(7.12, 7.32, 7.52, 7.72, 7.92) 

scala> 7.12 to(8, 0.5) 
res2: scala.collection.immutable.NumericRange[Double] = NumericRange(7.12, 7.62) 

scala> 7.12 to(8, 0.3) 
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result. 
at java.math.BigDecimal.divide(BigDecimal.java:1525) 
at java.math.BigDecimal.divide(BigDecimal.java:1558) 
at scala.math.BigDecimal.$div(BigDecimal.scala:228) 
at scala.math.Numeric$BigDecimalAsIfIntegral$class.quot(Numeric.scala:156) 
at scala.math.Numeric$BigDecimalAsIfIntegral$.quot(Numeric.scala:163) 
at scala.math.Numeric$BigDecimalAsIfIntegral$.quot(Numeric.scala:163) 
at scala.math.Integral$IntegralOps.$div$percent(Integral.scala:23) 
at scala.collection.immutable.NumericRange.genericLength(NumericRange.scala:104) 
at scala.collection.immutable.NumericRange.<init>(NumericRange.scala:63) 
at scala.collection.immutable.NumericRange$Inclusive.<init>(NumericRange.scala:209) 
at ... 

उत्तर

7

इस BigDecimal कहाँ वास्तव में से आता है? Range.scala

// Double works by using a BigDecimal under the hood for precise 
    // stepping, but mapping the sequence values back to doubles with 
    // .doubleValue. This constructs the BigDecimals by way of the 
    // String constructor (valueOf) instead of the Double one, which 
    // is necessary to keep 0.3d at 0.3 as opposed to 
    // 0.299999999999999988897769753748434595763683319091796875 or so. 
    object Double { 
    implicit val bigDecAsIntegral = scala.Numeric.BigDecimalAsIfIntegral 
    implicit val doubleAsIntegral = scala.Numeric.DoubleAsIfIntegral 
    def toBD(x: Double): BigDecimal = scala.BigDecimal valueOf x 

    def apply(start: Double, end: Double, step: Double) = 
     BigDecimal(toBD(start), toBD(end), toBD(step)) mapRange (_.doubleValue) 

    def inclusive(start: Double, end: Double, step: Double) = 
     BigDecimal.inclusive(toBD(start), toBD(end), toBD(step)) mapRange (_.doubleValue) 
    } 

के साथ शुरू करो और NumericRange.scala में ले जाएँ:

// Motivated by the desire for Double ranges with BigDecimal precision, 
    // we need some way to map a Range and get another Range. This can't be 
    // done in any fully general way because Ranges are not arbitrary 
    // sequences but step-valued, so we have a custom method only we can call 
    // which we promise to use responsibly. 
    // 
    // The point of it all is that 
    // 
    // 0.0 to 1.0 by 0.1 
    // 
    // should result in 
    // 
    // NumericRange[Double](0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0) 
    // 
    // and not 
    // 
    // NumericRange[Double](0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9) 
    // 
    // or perhaps more importantly, 
    // 
    // (0.1 to 0.3 by 0.1 contains 0.3) == true 
    // 
    private[immutable] def mapRange[A](fm: T => A)(implicit unum: Integral[A]): NumericRange[A] = {  
    val self = this 

    // XXX This may be incomplete. 
    new NumericRange[A](fm(start), fm(end), fm(step), isInclusive) { 
     def copy(start: A, end: A, step: A): NumericRange[A] = 
     if (isInclusive) NumericRange.inclusive(start, end, step) 
     else NumericRange(start, end, step) 

     private val underlyingRange: NumericRange[T] = self 
     override def foreach[U](f: A => U) { underlyingRange foreach (x => f(fm(x))) } 
     override def isEmpty = underlyingRange.isEmpty 
     override def apply(idx: Int): A = fm(underlyingRange(idx)) 
     override def containsTyped(el: A) = underlyingRange exists (x => fm(x) == el) 
    } 
    } 

आकाश गिर अगर toBD एक MathContext कि गोलाई अनुमति इस्तेमाल किया? दो बुराइयों में से कम कौन सा है? मैं उस प्रश्न को @extempore पर रोक दूंगा।

+2

इस मामले में एक सरल उपाय BigDecimal निर्माता का उपयोग करने के पूर्णांकन लागू करने के लिए किया जाएगा। मैं पूर्णांकन में किया जाना चाहिए लगता है,() और समावेशी() रेंज कक्षा में डबल वस्तु के तरीकों को लागू करने के बाद से यह है कि जहां हम जानते हैं कि प्रत्याशित प्रतिफल श्रेणी केवल डबल्स का आयोजन करेगा। ओडर्स्की को कौन बताना चाहिए? :-) –

+0

@ संक्षिप्त नाम: मुझे समझ में नहीं आता कि आप स्काला लाइब्रेरी से विशेष रूप से उपयोग की जाने वाली सुविधाओं का उपयोग कैसे करते हैं (विशेष रूप से निजी मैपेंज का उपयोग कहां किया जाता है?)। क्या आप एक उदाहरण दे सकते हैं? – jopasserat

7

मैं सिर्फ Scala Forum पर answered this:

scala> import java.math.{ MathContext => MC, RoundingMode => RM } 
import java.math.{MathContext=>MC, RoundingMode=>RM} 

scala> val mc1 = new MC(6, RM.HALF_DOWN) 
mc1: java.math.MathContext = precision=6 roundingMode=HALF_DOWN 


scala> val a1 = BigDecimal(1, mc1) 
a1: scala.math.BigDecimal = 1 

scala> val b1 = BigDecimal(3, mc1) 
b1: scala.math.BigDecimal = 3 

scala> a1/b1 
res10: scala.math.BigDecimal = 0.333333 


scala> val a2 = BigDecimal(1, MC.DECIMAL128) 
a2: scala.math.BigDecimal = 1 

scala> val b2 = BigDecimal(3, MC.DECIMAL128) 
b2: scala.math.BigDecimal = 3 

scala> a2/b2 
res11: scala.math.BigDecimal = 0.3333333333333333333333333333333333 

मैं कर रहा हूँ नहीं यकीन है कि, हालांकि, इस है परिणाम आप चाहते हैं:

scala> def BD128(d: Double): BigDecimal = BigDecimal(d, MC.DECIMAL128) 
BD128: (d: Double)BigDecimal 

scala> BD128(7.12) to(BD128(8), BD128(0.3)) 
res10: scala.collection.immutable.NumericRange.Inclusive[BigDecimal] = NumericRange(7.12, 7.42, 7.72) 
+1

WO के लिए धन्यवाद। क्या आप इसे RichDouble में एक बग के रूप में देखते हैं? यदि नहीं, तो क्या प्रोग्रामर यह जानना चाहते हैं कि BigDecimal का उपयोग RichDouble में लागू करने के लिए किया जाता है, और इस त्रुटि से कैसे बचें? –

+1

मैं नहीं कह सकता कि मुझे लगता है कि यह संख्यात्मक प्रकारों के साथ एक समस्या है। संभवत: आप 'NumericRange' पर उंगली बात कर सकता है, लेकिन वहाँ इस व्यवहार को रोकने के लिए, उस पर एक सटीक लागू करने के लिए होगा किसी भी' BigDecimal' एक सीमा का निर्माण करने के लिए प्रयोग किया जाता है, तो कुल मिलाकर, मैं नहीं, यह एक बग नहीं है कहेंगे, बस कुछ ग्राहकों को इसके बारे में पता होना चाहिए। –

+1

मुझे अभी भी यह अजीब लगता है। आप सुझाव है कि एक होना चाहिए न करें: (BD128 (7.12) के लिए ए) हमेशा का उपयोग करें WO आप प्रस्तुत (BD128 (8), BD128 (0.3))) या ख) RichDouble.to विधि का उपयोग करें, लेकिन यह है कि के बारे में पता किया जा रहा है उड़ा सकता है, और जब यह डब्ल्यूओ का उपयोग करता है? –

2

ठीक है, कुछ भी नहीं यहाँ क्या हुआ के बाद से मैं collection.immutable.Range में निम्नलिखित परिवर्तन का प्रस्ताव:

object Double { 
    implicit val bigDecAsIntegral = scala.Numeric.BigDecimalAsIfIntegral 
    implicit val doubleAsIntegral = scala.Numeric.DoubleAsIfIntegral 
    def toBD(x: Double): BigDecimal = { 
     // Let's round this Double to prevent 
     // error caused by /% in NumericRange 
     scala.BigDecimal(x, MC.DECIMAL128) 
    } 

    def apply(start: Double, end: Double, step: Double) = 
     BigDecimal(toBD(start), toBD(end), toBD(step)) mapRange (_.doubleValue) 

    def inclusive(start: Double, end: Double, step: Double) = 
     BigDecimal.inclusive(toBD(start), toBD(end), toBD(step)) mapRange (_.doubleValue) 
    } 

हम इस के लिए एक ठीक की जरूरत है, और मुझे परवाह नहीं है अगर कुछ छोटे परिशुद्धता खो दिया है।

संबंधित मुद्दे