Answers:
您可以使用scala.math.BigDecimal
:
BigDecimal(1.23456789).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
"%.2f".format(x).toDouble
那种情况。仅慢2倍,您只需要使用一个已经知道的库即可。
scala> "%.2f".format(0.714999999999).toDouble
res13: Double = 0.71
但是scala> "%.2f".format(0.715).toDouble
res14: Double = 0.72
。
这是没有BigDecimals的另一种解决方案
截短:
(math floor 1.23456789 * 100) / 100
回合:
(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 }
舍入函数可以执行类似的操作,这次使用currying:
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)
NaN
,不是吗?
floor
在于,truncateAt(1.23456789, 8)
它将返回,1.23456788
同时roundAt(1.23456789, 8)
将返回正确的值1.23456789
由于尚无人提及%
操作员,因此来了。它仅进行截断,并且您不能依赖于返回值不具有浮点数错误,但是有时很方便:
scala> 1.23456789 - (1.23456789 % 0.01)
res4: Double = 1.23
26.257391515826225 - 0.057391515826223094 = 26.200000000000003
编辑:修复了@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
p10
用数组查找替换递归函数也可能很有用:数组将使内存消耗增加约200个字节,但每次调用可能节省数次迭代。
您可以使用隐式类:
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))
}
对于那些感兴趣的人,这里有些时候提供建议的解决方案...
Rounding
Java Formatter: Elapsed Time: 105
Scala Formatter: Elapsed Time: 167
BigDecimal Formatter: Elapsed Time: 27
Truncation
Scala custom Formatter: Elapsed Time: 3
截断速度最快,其次是BigDecimal。请记住,这些测试是在运行norma 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())
}
}
}
}
...truncate or round a Double
。
doubleParts.tail
使用字符串“。” 获取and concat 的前两个字符。并doubleParts. head
和解析翻番。
toString.split(".")
和doubleParts.head/tail
建议可能会受到额外的数组分配以及字符串连接的影响。需要进行测试以确保。
最近,我遇到了类似的问题,并使用以下方法解决了它
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和隐含\ explicit选项,我有几个重载函数。请注意,在函数重载的情况下,您需要明确提及返回类型。
使用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
如果您关心性能,我不会使用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)
我将坚持Kaito建议的数学操作。
您可以这样做: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()
使用“舍入”模式的首选项。