Scala 双精度和精度
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11106886/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Scala Doubles, and Precision
提问by richsoni
Is there a function that can truncate or round a Double? At one point in my code I would like a number like: 1.23456789to be rounded to 1.23
是否有可以截断或舍入 Double 的函数?在我的代码中,我想要一个数字,例如:1.23456789四舍五入到1.23
回答by Travis Brown
You can use scala.math.BigDecimal:
您可以使用scala.math.BigDecimal:
BigDecimal(1.23456789).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
There are a number of other rounding modes, which unfortunately aren't very well documented at present (although their Java equivalents are).
还有许多其他的舍入模式,遗憾的是目前还没有很好的记录(尽管它们的 Java 等效项是)。
回答by Kaito
Here's another solution without BigDecimals
这是另一个没有 BigDecimals 的解决方案
Truncate:
截短:
(math floor 1.23456789 * 100) / 100
Round:
圆形的:
(math rint 1.23456789 * 100) / 100
Or for any double n and precision p:
或者对于任何双精度 n 和精度 p:
def truncateAt(n: Double, p: Int): Double = { val s = math pow (10, p); (math floor n * s) / s }
Similar can be done for the rounding function, this time using currying:
可以对舍入函数进行类似操作,这次使用柯里化:
def roundAt(p: Int)(n: Double): Double = { val s = math pow (10, p); (math round n * s) / s }
which is more reusable, e.g. when rounding money amounts the following could be used:
这是更可重用的,例如,当四舍五入金额时,可以使用以下内容:
def roundAt2(n: Double) = roundAt(2)(n)
回答by akauppi
Since no-one mentioned the %operator yet, here comes. It only does truncation, and you cannot rely on the return value not to have floating point inaccuracies, but sometimes it's handy:
由于还没有人提到%运营商,所以来了。它只进行截断,您不能依赖返回值来避免浮点数不准确,但有时它很方便:
scala> 1.23456789 - (1.23456789 % 0.01)
res4: Double = 1.23
回答by blue-sky
How about :
怎么样 :
val value = 1.4142135623730951
//3 decimal places
println((value * 1000).round / 1000.toDouble)
//4 decimal places
println((value * 10000).round / 10000.toDouble)
回答by Rex Kerr
Edit: fixed the problem that @ryryguy pointed out. (Thanks!)
编辑:修复了@ryryguy 指出的问题。(谢谢!)
If you want it to be fast, Kaito has the right idea. math.powis slow, though. For any standard use you're better off with a recursive function:
如果你想快点,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
}
}
This is about 10x faster if you're within the range of Long(i.e 18 digits), so you can round at anywhere between 10^18 and 10^-18.
如果您在 范围内Long(即 18 位数字),这大约快 10 倍,因此您可以在 10^18 和 10^-18 之间的任何位置取整。
回答by Mitrakov Artem
You may use implicit classes:
您可以使用隐式类:
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))
}
回答by cevaris
For those how are interested, here are some times for the suggested solutions...
对于那些有兴趣的人,这里有一些建议的解决方案......
Rounding
Java Formatter: Elapsed Time: 105
Scala Formatter: Elapsed Time: 167
BigDecimal Formatter: Elapsed Time: 27
Truncation
Scala custom Formatter: Elapsed Time: 3
Truncation is the fastest, followed by BigDecimal. Keep in mind these test were done running norma scala execution, not using any benchmarking tools.
截断是最快的,其次是 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())
}
}
}
}
回答by Khalid Saifullah
Recently, I faced similar problem and I solved it using following approach
最近,我遇到了类似的问题,并使用以下方法解决了它
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)
I used thisSO issue. I have couple of overloaded functions for both Float\Double and implicit\explicit options. Note that, you need to explicitly mention the return type in case of overloaded functions.
我使用了这个SO 问题。我有两个 Float\Double 和隐式\显式选项的重载函数。请注意,在重载函数的情况下,您需要明确提及返回类型。
回答by bigonazzi
I wouldn't use BigDecimal if you care about performance. BigDecimal converts numbers to string and then parses it back again:
如果您关心性能,我不会使用 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)
I'm going to stick to math manipulations as Kaitosuggested.
我将坚持使用Kaito建议的数学操作。
回答by R Sun
It's actually very easy to handle using Scala finterpolator - https://docs.scala-lang.org/overviews/core/string-interpolation.html
使用 Scalaf插值器实际上很容易处理- https://docs.scala-lang.org/overviews/core/string-interpolation.html
Suppose we want to round till 2 decimal places:
假设我们要四舍五入到小数点后两位:
scala> val sum = 1 + 1/4D + 1/7D + 1/10D + 1/13D
sum: Double = 1.5697802197802198
scala> println(f"$sum%1.2f")
1.57

