是否有可以截断或四舍五入Double类型的函数? 在我的代码的某个地方,我希望像 1.23456789
这样的数字被舍入为 1.23
您可以使用scala.math.BigDecimal
:
BigDecimal(1.23456789).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
除了 四舍五入模式 外,还有许多其他的取整模式,可惜目前它们的文档不是很好(虽然Java 中对应的模式有文档)。
scala> "%.2f".format(0.714999999999).toDouble
的结果是res13: Double = 0.71
,但scala> "%.2f".format(0.715).toDouble
的结果是res14: Double = 0.72
。请问您需要解释什么吗? - Rex Kerr这里是另一种不使用BigDecimal的解决方案
截断:
(math floor 1.23456789 * 100) / 100
四舍五入(参见rint
):
(math rint 1.23456789 * 100) / 100
或者对于任何双精度数n和精度p:
def truncateAt(n: Double, p: Int): Double = { val s = math pow (10, p); (math floor n * s) / s }
同样的方法也可以用于 rounding 函数,这次使用柯里化:
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)
floor
的问题在于 truncateAt(1.23456789, 8)
返回的是 1.23456788
,而 roundAt(1.23456789, 8)
则会返回正确的值 1.23456789
。 - Todor Kolev由于没有人提及%
运算符,所以在这里介绍一下。它只进行截断,并且您不能指望返回值不具有浮点不准确性,但有时它很方便:
scala> 1.23456789 - (1.23456789 % 0.01)
res4: Double = 1.23
26.257391515826225 - 0.057391515826223094 = 26.200000000000003
- kubudi怎么样:
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位小数。 - robert"%.4f".format(myDoubleNumber)
。示例:"%.4f".format(1.99999)
将返回 2.0000
"%.4f".format(1.23499)
将返回 1.2350
当然,结果是字符串,仅适用于渲染。 - NKM使用Scala f
内插器处理实际上非常容易-https://docs.scala-lang.org/overviews/core/string-interpolation.html
假设我们想要保留两位小数:
scala> val sum = 1 + 1/4D + 1/7D + 1/10D + 1/13D
sum: Double = 1.5697802197802198
scala> println(f"$sum%1.2f")
1.57
f"$y$xf"
但不行。 - Jason Politis编辑:修复了@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之间任意舍入。
scala> def r5(x:Double) = math.round(x*100000)*0.000001; r5(0.23515)
==> res12: Double = 0.023514999999999998
。相反,应该通过除以有效数字来处理:math.round(x*100000)/100000.0
。 - ryryguyp10
函数替换为数组查找可能也很有用:该数组会增加约 200 字节的内存消耗,但可能每次调用可以节省多个迭代。 - Levi RamseyRounding
Java Formatter: Elapsed Time: 105
Scala Formatter: Elapsed Time: 167
BigDecimal Formatter: Elapsed Time: 27
Truncation
Scala custom Formatter: Elapsed Time: 3
截断(Truncation)是最快的,其次是BigDecimal。请记住,这些测试是在正常的Scala执行中进行的,而不是使用任何基准测试工具。
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())
}
}
}
}
...截断或四舍五入一个Double值
。 - cevarisdoubleParts.tail
的前两个字符并与字符串 "." 和 doubleParts.head
连接,然后解析为双精度浮点数。 - Ravinder PayaltoString.split(".")
和doubleParts.head/tail
建议可能会导致额外的数组分配和字符串连接。不过还需要测试以确保。 - cevarisString
类型???在哪个世界里这是一种简化方式???至少需要进行2次额外的强制类型转换,即Double -> String 和 String -> Double(每部分潜在地需要2次...)。 - Randomness Slayerimport 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))
}
这个帖子里的回答都很好。为了更好地展示区别,这里只是一个例子。我把它放在这里的原因是因为在我的工作中需要确保数字不是半数上调:
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
(其中必须显式定义舍入模式))。
最近,我遇到了类似的问题,我用以下方法解决了它
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)
我使用了这个Stack Overflow问题的解决方法。我有几个重载函数,包括Float\Double和implicit\explicit选项。请注意,在重载函数的情况下,需要明确指定返回类型。
1.24
吗? - Sergey Bushmanov