133

Doubleを切り捨てたり丸めたりできる関数はありますか?私のコードのある時点で、次のような数値が1.23456789必要です。1.23

4

15 に答える 15

169

あなたが使用することができますscala.math.BigDecimal

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

他にも多くの丸めモードがありますが、残念ながら現時点では十分に文書化されていません(ただし、Javaに相当するものは文書化されています)。

于 2012-06-19T18:22:27.870 に答える
81

BigDecimalsを使用しない別のソリューションは次のとおりです

切り捨て:

(math floor 1.23456789 * 100) / 100

ラウンド:

(math rint 1.23456789 * 100) / 100

または、任意のdouble nおよび精度pの場合:

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)
于 2012-06-19T18:56:21.737 に答える
36

まだ誰も%オペレーターについて言及していないので、ここに来ます。これは切り捨てを行うだけであり、浮動小数点の不正確さがないように戻り値に依存することはできませんが、便利な場合があります。

scala> 1.23456789 - (1.23456789 % 0.01)
res4: Double = 1.23
于 2013-01-10T20:02:51.573 に答える
13

どうですか:

 val value = 1.4142135623730951

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

//4 decimal places
println((value * 10000).round / 10000.toDouble)
于 2014-06-18T20:17:26.300 に答える
7

編集:@ryryguyが指摘した問題を修正しました。(ありがとう!)

あなたがそれを速くしたいのなら、Kaitoは正しい考えを持っています。 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桁)の場合は約10倍高速であるため、10^18から10^-18の間の任意の場所で丸めることができます。

于 2012-06-19T19:29:18.493 に答える
5

どのように興味を持っている人のために、ここに提案された解決策のためのいくつかの時間があります...

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

Truncation
Scala custom Formatter: Elapsed Time: 3 

切り捨てが最も速く、BigDecimalがそれに続きます。これらのテストは、ベンチマークツールを使用せずに、normascalaの実行を実行して実行されたことを覚えておいてください。

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())
      }
    }
  }

}
于 2016-05-23T22:55:04.517 に答える
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))
}
于 2014-06-20T09:39:14.570 に答える
4

fScala補間器を使用すると、実際には非常に簡単に処理できます-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
于 2020-02-21T12:57:58.450 に答える
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)

私はこのSOの問題を使用しました。Float\Doubleオプションとimplicit\explicitオプションの両方にオーバーロードされた関数がいくつかあります。オーバーロードされた関数の場合は、戻り型を明示的に指定する必要があることに注意してください。

于 2013-05-17T14:14:14.933 に答える
2

これらは、このスレッドでのすばらしい答えです。違いをわかりやすくするために、ここに例を示します。私が仕事中にここにb/cを置いた理由は、数字が半分になっていない必要があります:

    import org.apache.spark.sql.types._
    val values = List(1.2345,2.9998,3.4567,4.0099,5.1231)
    val df = values.toDF
    df.show()
    +------+
    | value|
    +------+
    |1.2345|
    |2.9998|
    |3.4567|
    |4.0099|
    |5.1231|
    +------+

    val df2 = df.withColumn("floor_val", floor(col("value"))).
    withColumn("dec_val", col("value").cast(DecimalType(26,2))).
    withColumn("floor2", (floor(col("value") * 100.0)/100.0).cast(DecimalType(26,2)))

    df2.show()
+------+---------+-------+------+
| value|floor_val|dec_val|floor2|
+------+---------+-------+------+
|1.2345|        1|   1.23|  1.23|
|2.9998|        2|   3.00|  2.99|
|3.4567|        3|   3.46|  3.45|
|4.0099|        4|   4.01|  4.00|
|5.1231|        5|   5.12|  5.12|
+------+---------+-------+------+

floor関数フロアは、現在の値よりも小さい最大の整数になります。DecimalTypeデフォルトでは、必要HALF_UPな精度にカットするだけでなく、モードが有効になります。モードを使用せずに特定の精度にカットしたい場合はHALF_UP、代わりに上記のソリューションを使用できます(またはscala.math.BigDecimal(丸めモードを明示的に定義する必要がある場合)を使用します。

于 2021-02-01T02:43:07.923 に答える
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)

怪盗が提案したように、私は数学の操作に固執するつもりです。

于 2017-01-10T15:11:23.613 に答える
0

少し奇妙ですがいいです。BigDecimalではなくStringを使用します

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
}
于 2017-07-09T16:52:14.627 に答える
0

あなたができること:Math.round(<double precision value> * 100.0) / 100.0 しかし、Math.roundは最速ですが、小数点以下の桁数が非常に多い(例:round(1000.0d、17))または大きな整数部分(例:round(90080070060.1d、9))のコーナーケースではうまく機能しません)。

Bigdecimalを使用すると、値が文字列に変換されるため少し非効率的ですが、より安心 BigDecimal(<value>).setScale(<places>, RoundingMode.HALF_UP).doubleValue() できます。丸めモードの設定を使用してください。

興味があり、なぜこれが起こるのかをもっと詳しく知りたい場合は、これを読むことができます: ここに画像の説明を入力してください

于 2019-04-08T11:53:49.747 に答える
0

質問は特にdoubleの丸めを指定しているので、これは大きな整数または過剰な文字列または数値演算を処理するよりもはるかに簡単に思えます。

"%.2f".format(0.714999999999).toDouble
于 2021-02-04T14:19:47.727 に答える
0

以前の答えは次のとおりです。

  • 明らかに間違っています。たとえば、math.floorを使用しても、負の値では機能しません。
  • 不必要に複雑。

@kaitoの回答に基づく提案は次のとおりです(まだコメントできません):

def truncateAt(x: Double, p: Int): Double = {
    val s = math.pow(10, p)
    (x * s).toInt / s
}

toIntは、正の値と負の値に対して機能します。

于 2021-07-27T17:04:54.327 に答える