स्काला डबल्स, और प्रेसिजन


118

क्या कोई फ़ंक्शन है जो एक डबल को छोटा या गोल कर सकता है? मेरे कोड में एक बिंदु पर मुझे एक संख्या पसंद होगी: 1.23456789को गोल किया जाएगा1.23


9
सभी उत्तरों को देखने के बाद मुझे लगता है कि संक्षिप्त उत्तर नहीं है? :)
मार्सेलस वालेस

4
@Gorgorg आपने मुझे हँसाया। मैं अन्य संख्यात्मक-भारी भाषाओं से स्काला के लिए नया हूं, और मेरा जबड़ा इस धागे को पढ़ते हुए लगभग हिट हो गया है। यह एक प्रोग्रामिंग भाषा के लिए मामलों की एक पागल स्थिति है।
ely

जवाबों:


150

आप उपयोग कर सकते हैं scala.math.BigDecimal:

BigDecimal(1.23456789).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble

कई अन्य राउंडिंग मोड हैं , जो दुर्भाग्य से वर्तमान में बहुत अच्छी तरह से प्रलेखित नहीं हैं (हालांकि उनके जावा समकक्ष हैं )।


5
काफी संभावना है, मैं कहूंगा। ग्रिड या वित्त से जुड़ी किसी भी चीज के लिए राउंडिंग और प्रदर्शन की आवश्यकता हो सकती है।
रेक्स केर

28
मुझे लगता है कि ऐसे लोग हैं जिनके लिए एक क्लूनी लाइब्रेरी के लिए एक लंबा कॉल सरल गणित की तुलना में अधिक समझ में आता है। मैं "%.2f".format(x).toDoubleउस मामले में सिफारिश करूंगा । केवल 2x धीमा, और आपको केवल एक पुस्तकालय का उपयोग करना होगा जिसे आप पहले से जानते हैं।
रेक्स केर

6
@RexKerr, आप इस मामले में राउंड नहीं कर रहे हैं .. बस ट्रंकटिंग।
जोस लील

16
@ JoséLeal - हुह? scala> "%.2f".format(0.714999999999).toDouble res13: Double = 0.71लेकिन scala> "%.2f".format(0.715).toDouble res14: Double = 0.72
रेक्स केर

5
@RexKerr मैं आपके string.format तरीके को पसंद करता हूं, लेकिन स्थानों में sa खान (फिनिश), ध्यान रखना होगा कि रूट लोकेल को ठीक किया जाए। जैसे "% .2f" .formatLocal (java.util.Locale.ROOT, x .toDouble। ऐसा लगता है, प्रारूप 'का उपयोग करता है', 'लोकेल की वजह से जबकि डबलडॉग इसे लेने में सक्षम नहीं है और नंबरफॉरमैट अपवाद को फेंकता है। यह पाठ्यक्रम इस बात पर आधारित है कि आपका कोड कहाँ चलाया जा रहा है , न कि यह कहाँ विकसित किया गया है।
एकुप्पी

77

यहाँ BigDecimals के बिना एक और समाधान है

छोटा करें:

(math floor 1.23456789 * 100) / 100

गोल:

(math rint 1.23456789 * 100) / 100

या किसी भी डबल एन और सटीक पी के लिए:

def truncateAt(n: Double, p: Int): Double = { val s = math pow (10, p); (math floor n * s) / s }

इसी तरह से राउंडिंग फ़ंक्शन के लिए किया जा सकता है, इस बार करी का उपयोग करके:

def roundAt(p: Int)(n: Double): Double = { val s = math pow (10, p); (math round n * s) / s }

जो अधिक पुन: प्रयोज्य है, उदाहरण के लिए, जब धनराशि को गोल किया जाता है तो निम्नलिखित का उपयोग किया जा सकता है:

def roundAt2(n: Double) = roundAt(2)(n)

8
राउंडअट 2 को राउंड एआट 2 होना चाहिए (एन: डबल) = राउंडअट (2) (एन) नहीं?
C4stor

ऐसा लगता NaNहै कि यह गलत परिणाम नहीं है?
जंगोरीकी

के साथ समस्या floorयह है truncateAt(1.23456789, 8)वापस आ जाएगी 1.23456788, जबकि roundAt(1.23456789, 8)का सही मूल्य वापस आ जाएगी1.23456789
टोडोर कोलेव

35

चूंकि किसी ने भी %ऑपरेटर का उल्लेख नहीं किया है, यहाँ आता है। यह केवल काट-छाँट करता है, और आप रिटर्न वैल्यू पर भरोसा नहीं कर सकते कि फ्लोटिंग पॉइंट अशुद्धि है, लेकिन यह आसान है:

scala> 1.23456789 - (1.23456789 % 0.01)
res4: Double = 1.23

2
हालांकि यह मेरा अपना उत्तर है, यह अनुशंसा नहीं करेंगे: @ryryguy द्वारा किसी अन्य उत्तर की टिप्पणी में उल्लिखित समान अशुद्धि के मुद्दे यहां भी प्रभावित करते हैं। Java ROOT लोकेल के साथ string.format का उपयोग करें (मैं उस बारे में टिप्पणी करूंगा)।
एकुप्पी

यह सही है अगर आपको केवल मूल्य प्रदान करने की आवश्यकता है और बाद के कार्यों में इसका उपयोग कभी न करें। धन्यवाद
अलेक्जेंडर अरेंडर

3
यहाँ कुछ मजेदार है: 26.257391515826225 - 0.057391515826223094 = 26.200000000000003
kubudi

12

कैसा रहेगा :

 val value = 1.4142135623730951

//3 decimal places
println((value * 1000).round / 1000.toDouble)

//4 decimal places
println((value * 10000).round / 10000.toDouble)

बहुत साफ समाधान। यहाँ ट्रंकेशन के लिए मेरा है: ((1.949 * 1000).toInt - ((1.949 * 1000).toInt % 10)) / 1000.toDoubleहालांकि यह बहुत अधिक परीक्षण नहीं किया। यह कोड 2 दशमलव स्थानों को करेगा।
रोबर्ट

7

संपादित करें: @ryryguy ने इंगित की गई समस्या को ठीक किया। (धन्यवाद!)

यदि आप चाहते हैं कि यह तेज़ हो, तो काइटो के पास सही विचार है। math.powहालांकि धीमा है। किसी भी मानक उपयोग के लिए आप पुनरावर्ती फ़ंक्शन के साथ बेहतर हैं:

def trunc(x: Double, n: Int) = {
  def p10(n: Int, pow: Long = 10): Long = if (n==0) pow else p10(n-1,pow*10)
  if (n < 0) {
    val m = p10(-n).toDouble
    math.round(x/m) * m
  }
  else {
    val m = p10(n).toDouble
    math.round(x*m) / m
  }
}

यदि आप Long(यानी 18 अंक) की सीमा के भीतर हैं, तो यह लगभग 10x तेज़ है , इसलिए आप 10 ^ 18 और 10 ^ -18 के बीच कहीं भी चक्कर लगा सकते हैं।


3
बाहर देखें, पारस्परिक द्वारा गुणा करना मज़बूती से काम नहीं करता है, क्योंकि यह एक डबल के रूप में विश्वसनीय रूप से प्रतिनिधित्व करने योग्य नहीं हो सकता है: scala> def r5(x:Double) = math.round(x*100000)*0.000001; r5(0.23515)=>> res12: Double = 0.023514999999999998। इसके बजाय महत्व से विभाजित करें:math.round(x*100000)/100000.0
ryryguy

यह p10एक सरणी लुकअप के साथ पुनरावर्ती फ़ंक्शन को बदलने के लिए भी उपयोगी हो सकता है : सरणी लगभग 200 बाइट्स द्वारा मेमोरी की खपत में वृद्धि करेगी, लेकिन संभवतः प्रति कॉल कई पुनरावृत्तियों को बचाएगी।
लेवी रामसे

4

आप निहित वर्गों का उपयोग कर सकते हैं:

import scala.math._

object ExtNumber extends App {
  implicit class ExtendedDouble(n: Double) {
    def rounded(x: Int) = {
      val w = pow(10, x)
      (n * w).toLong.toDouble / w
    }
  }

  // usage
  val a = 1.23456789
  println(a.rounded(2))
}

1
कृपया निर्दिष्ट करें कि यह विधि केवल ट्रंकिंग के लिए है न कि ठीक से गोलाई में।
bobo32

4

जो लोग रुचि रखते हैं, उनके लिए सुझाए गए समाधानों के लिए यहां कुछ समय दिया गया है ...

Rounding
Java Formatter: Elapsed Time: 105
Scala Formatter: Elapsed Time: 167
BigDecimal Formatter: Elapsed Time: 27

Truncation
Scala custom Formatter: Elapsed Time: 3 

ट्रडनेशन सबसे तेज़ है, इसके बाद बिगडेसीमल है। ध्यान रखें कि ये परीक्षण किसी भी बेंचमार्किंग टूल का उपयोग न करते हुए नॉर्मा स्कैला निष्पादन चलाने के लिए किए गए थे।

object TestFormatters {

  val r = scala.util.Random

  def textFormatter(x: Double) = new java.text.DecimalFormat("0.##").format(x)

  def scalaFormatter(x: Double) = "$pi%1.2f".format(x)

  def bigDecimalFormatter(x: Double) = BigDecimal(x).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble

  def scalaCustom(x: Double) = {
    val roundBy = 2
    val w = math.pow(10, roundBy)
    (x * w).toLong.toDouble / w
  }

  def timed(f: => Unit) = {
    val start = System.currentTimeMillis()
    f
    val end = System.currentTimeMillis()
    println("Elapsed Time: " + (end - start))
  }

  def main(args: Array[String]): Unit = {

    print("Java Formatter: ")
    val iters = 10000
    timed {
      (0 until iters) foreach { _ =>
        textFormatter(r.nextDouble())
      }
    }

    print("Scala Formatter: ")
    timed {
      (0 until iters) foreach { _ =>
        scalaFormatter(r.nextDouble())
      }
    }

    print("BigDecimal Formatter: ")
    timed {
      (0 until iters) foreach { _ =>
        bigDecimalFormatter(r.nextDouble())
      }
    }

    print("Scala custom Formatter (truncation): ")
    timed {
      (0 until iters) foreach { _ =>
        scalaCustom(r.nextDouble())
      }
    }
  }

}

1
प्रिय scalaCustom बंद गोल नहीं है, यह सिर्फ छोटा है
रविन्दर पायल

हम्म, ओपी गोलाई या छंटनी के लिए विशिष्ट नहीं था; ...truncate or round a Double
केवरिस

लेकिन मेरी राय में राउंडिंग फ़ंक्शन के साथ ट्रंकिंग फ़ंक्शन की गति / निष्पादन समय की तुलना अपर्याप्त है। इसलिए मैंने आपसे पाठक से यह स्पष्ट करने को कहा है कि कस्टम फीचर केवल छोटा है। और आपके द्वारा उल्लिखित ट्रंकट / कस्टम फ़ंक्शन को और सरल बनाया जा सकता है। वैल डबलपार्ट्स = डबल। .String.split ("।") अब doubleParts.tailस्ट्रिंग्स के साथ और कॉनैट के पहले दो वर्ण प्राप्त करें ""। और doubleParts. headडबल करने के लिए पार्स।
रविंदर पायल

1
अद्यतन, बेहतर देखो? आपके सुझाव toString.split(".")और doubleParts.head/tailसुझाव भी अतिरिक्त सरणी आवंटन और स्ट्रिंग संघनन से पीड़ित हो सकते हैं। हालांकि यह सुनिश्चित करने के लिए परीक्षण करने की आवश्यकता होगी।
केवरिस

3

हाल ही में, मुझे इसी तरह की समस्या का सामना करना पड़ा और मैंने इसे निम्नलिखित दृष्टिकोण का उपयोग करके हल किया

def round(value: Either[Double, Float], places: Int) = {
  if (places < 0) 0
  else {
    val factor = Math.pow(10, places)
    value match {
      case Left(d) => (Math.round(d * factor) / factor)
      case Right(f) => (Math.round(f * factor) / factor)
    }
  }
}

def round(value: Double): Double = round(Left(value), 0)
def round(value: Double, places: Int): Double = round(Left(value), places)
def round(value: Float): Double = round(Right(value), 0)
def round(value: Float, places: Int): Double = round(Right(value), places)

मैं इस एसओ मुद्दे का इस्तेमाल किया । मेरे पास फ्लोट \ डबल और अंतर्निहित \ स्पष्ट दोनों विकल्पों के लिए अतिभारित कार्य हैं। ध्यान दें, आपको ओवरलोड कार्यों के मामले में स्पष्ट रूप से रिटर्न प्रकार का उल्लेख करना होगा।


इसके अलावा, आप Math.pow के बजाय सत्ता के लिए @ रेक्स-केर के दृष्टिकोण का उपयोग कर सकते हैं
खालिद सैफुल्लाह

2

स्काला fइंटरपोलर का उपयोग करना वास्तव में बहुत आसान है - https://docs.scala-lang.org/overviews/core/string-interpolation.html

मान लीजिए कि हम 2 दशमलव स्थानों तक चक्कर लगाना चाहते हैं:

scala> val sum = 1 + 1/4D + 1/7D + 1/10D + 1/13D
sum: Double = 1.5697802197802198

scala> println(f"$sum%1.2f")
1.57

1

यदि आप प्रदर्शन के बारे में परवाह करते हैं तो मैं BigDecimal का उपयोग नहीं करूंगा। BigDecimal संख्याओं को स्ट्रिंग में परिवर्तित करता है और फिर इसे फिर से पार्स करता है:

  /** Constructs a `BigDecimal` using the decimal text representation of `Double` value `d`, rounding if necessary. */
  def decimal(d: Double, mc: MathContext): BigDecimal = new BigDecimal(new BigDec(java.lang.Double.toString(d), mc), mc)

जैसा कि काइटो ने सुझाव दिया था कि मैं गणित में हेरफेर करने जा रहा हूं ।


0

थोड़ा अजीब लेकिन अच्छा। मैं स्ट्रिंग का उपयोग करता हूं न कि बिगडेसिमल का

def round(x: Double)(p: Int): Double = {
    var A = x.toString().split('.')
    (A(0) + "." + A(1).substring(0, if (p > A(1).length()) A(1).length() else p)).toDouble
}

0

तुम कर सकते हो:Math.round(<double precision value> * 100.0) / 100.0 लेकिन Math.round सबसे तेज़ है लेकिन यह कोने के मामलों में या तो बहुत अधिक संख्या में दशमलव स्थानों (जैसे राउंड (1000.0d, 17)) या बड़े पूर्णांक वाले भाग (जैसे राउंड (90080070060.1d, 9) के साथ बुरी तरह से टूट जाता है )।

Bigdecimal का उपयोग करें यह थोड़ा अक्षम है क्योंकि यह मूल्यों को स्ट्रिंग में परिवर्तित करता है लेकिन अधिक मध्ययुगीन: BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue() गोलाई मोड की अपनी प्राथमिकता का उपयोग करें।

यदि आप उत्सुक हैं और अधिक विस्तार से जानना चाहते हैं कि ऐसा क्यों होता है तो आप इसे पढ़ सकते हैं: यहां छवि विवरण दर्ज करें

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.