I'm trying to format floating point numbers with a maximum number of digits, but I don't want unnecessary trailing zeroes. I thought if I use g instead of f it would work (cf. this question)

def testF(d: Double) = f"$d%1.2f"
def testG(d: Double) = f"$d%1.2g"

Now this behaves rather strangely:

testF(3.1415)  // --> 3.14 ok
testF(3.1)     // --> 3.10 hmmm, don't want that zero

testG(3.1415)  // --> 3.1 what the ?

Ok, so perhaps I need to increase the digits by one for g:

def testG(d: Double) = f"$d%1.3g"

testG(3.1415)  // --> 3.14 ok
testG(3.1)     // --> 3.10 grmpf

So two questions—one, why the heck is g dropping one digit and doesn't seem to care about trailing zeroes? Two, how can I have

testX(3.1415)  // --> 3.14
testX(3.1)     // --> 3.1

?

有帮助吗?

解决方案

You could use the java DecimalFormat but it may not please the eye:

def testX(d: Double) = new java.text.DecimalFormat("#.##").format(d)

Also to answer your first question why the heck is g dropping one digit and doesn't seem to care about trailing zeroes

For the floating-point conversions 'e', 'E', and 'f' the precision is the number of digits after the decimal separator. If the conversion is 'g' or 'G', then the precision is the total number of digits in the resulting magnitude after rounding.

Formatter details

其他提示

As with the C world's printf, Java's (and Scala's) Formatter includes both minimum and maximum field widths (for the %s format specifier):

// Minimum field width -- right-aligned
scala> "%23s".format(23)
res0: String = "                     23"

// Minimum field width -- left-aligned
scala> "%-23s".format(23)
res1: String = "23                     "

// Maximum field width with right truncation
scala> "%.3s".format(12345)
res2: String = 123

// Minimum and maximum width -- right aligned
scala> "%3.3s".format(1)
res3: String = "  1"

// Minimum and maximum width -- left aligned
scala> "%-3.3s".format(1)
res4: String = "1  "

How about this version?

The use of Formattable requires the %s format, but you're given the formatting args to interpret as you like.

A Formattable can pop up in the middle of an f-interpolation, since the normal format just uses either toString or your custom formatTo.

package object succinctly {
  import java.util.{ Formattable, FormattableFlags, Formatter }
  import scala.language.implicitConversions
  implicit class Succinctly(val __thing: Double) extends AnyVal {
    @inline def succinctly(s: String): String = s format fmtable(__thing)
    @inline def succinctly: Formattable = fmtable(__thing)
  }
  private[this] val trailing = "(.*\\...*?)(0*)".r
  private[this] def fmtable(a: Double) = new Formattable {
    override def formatTo(formatter: Formatter, flags: Int, width: Int, precision: Int) = formatter.out append (
      if (precision <= 0) a.toInt.toString
      else s"%${width}.${precision}f" format a.asInstanceOf[java.lang.Double] match { case trailing(num, _) => num }
    )
  }
}

package succinctly {
  import scala.language.postfixOps
  import scala.math._
  object Test extends App {
    Console println (Pi succinctly "%1.2s")
    Console println (3.1 succinctly "%1.2s")
    Console println (3.0 succinctly "%1.2s")
    Console println f"${3.1 succinctly}%1.2s"
  }
}

It's also conceivable to write a custom interpolator that promotes doubles to trailless and changes %f to %s, though at some cost, since the f-interpolator is a macro.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top