是否有可以截断或舍入Double的函数?在我的代码中的某一点上,我希望将一个数字:1.23456789
四舍五入为1.23
答案 0 :(得分:126)
您可以使用scala.math.BigDecimal
:
BigDecimal(1.23456789).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
还有许多其他rounding modes,遗憾的是目前还没有很好地记录(尽管their Java equivalents are)。
答案 1 :(得分:69)
这是没有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(p: Int) = roundAt(2)(p)
答案 2 :(得分:30)
由于还没有人提到%
运营商,所以来了。它只是截断,你不能依赖返回值不会有浮点不准确,但有时它很方便:
scala> 1.23456789 - (1.23456789 % 0.01)
res4: Double = 1.23
答案 3 :(得分:8)
怎么样:
val value = 1.4142135623730951
//3 decimal places
println((value * 1000).round / 1000.toDouble)
//4 decimal places
println((value * 10000).round / 10000.toDouble)
答案 4 :(得分: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之间的任何地方进行舍入。
答案 5 :(得分: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))
}
答案 6 :(得分:4)
对于那些感兴趣的人,这里有一些建议的解决方案......
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())
}
}
}
}
答案 7 :(得分: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)
我使用了this SO问题。我有两个Float \ Double和implicit \ explicit选项的重载函数。请注意,在重载函数的情况下,需要明确提及返回类型。
答案 8 :(得分:2)
使用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
答案 9 :(得分: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
(你必须明确定义舍入模式)。
答案 10 :(得分: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)
我将坚持Kaito提出的数学操作。
答案 11 :(得分:0)
有点奇怪但很好。我使用String而不是BigDecimal
package com.openkm.config;
import java.io.IOException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;
import freemarker.cache.TemplateLoader;
import freemarker.template.TemplateException;
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/").setCachePeriod(604800); // One week
}
@Bean(name = "freeMarkerViewResolver")
public FreeMarkerViewResolver getFreeMarkerViewResolver() {
FreeMarkerViewResolver viewResolver = new FreeMarkerViewResolver();
viewResolver.setExposeSpringMacroHelpers(true);
viewResolver.setExposeRequestAttributes(true);
viewResolver.setExposeSessionAttributes(true);
viewResolver.setPrefix("/WEB-INF/ftl/");
viewResolver.setSuffix(".ftl");
viewResolver.setOrder(0);
viewResolver.setContentType("text/html; charset=UTF-8");
viewResolver.setCache(false);
viewResolver.setRequestContextAttribute("request");
return viewResolver;
}
}
答案 12 :(得分: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()
使用您偏爱的舍入模式。
答案 13 :(得分:0)
由于问题具体指定了双精度舍入,这似乎比处理大整数或过多的字符串或数字运算要简单得多。
"%.2f".format(0.714999999999).toDouble
答案 14 :(得分:0)
我认为以前的答案是:
这是基于@kaito 的回答的建议(我还不能发表评论):
def truncateAt(x: Double, p: Int): Double = {
val s = math.pow(10, p)
(x * s).toInt / s
}
toInt 适用于正值和负值。