2

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

?

Community
  • 1
  • 1
0__
  • 66,707
  • 21
  • 171
  • 266
  • Like [this](http://stackoverflow.com/questions/277772/avoid-trailing-zeroes-in-printf?lq=1) but for Scala/Java – 0__ Jun 13 '13 at 14:05
  • 3
    `f"$d%1.2f".reverse.dropWhile{ _ == '0' }.dropWhile{ _ == '.' }.reverse`. I don't think you can do it with formatting only. – senia Jun 13 '13 at 14:20
  • @0__ Did you try my method? You first commented that it didn't work but now the comment is gone. For me it works with `testX(3.1)` and without problems. – maba Jun 13 '13 at 14:47
  • This is interview question territory. I guess the Google interview question would be: Estimate how much time has been spent on printf formats since the unix epoch, expressed in man-eons (excuse the sexist term). Why is there no +1? We can pad zero but not ignore trailing? Are they saving us from doing evil wrt precision? – som-snytt Jun 13 '13 at 17:45
  • @maba - yes, I had a wrong reference in my REPL history which led me to believe it didn't work, but I figured it out and indeed it works. If there are no other suggestions more integrated with string interp, I will accept your answer. – 0__ Jun 13 '13 at 20:11
  • I thought you wanted integration with f"s". Since you mentioned it, I like the idea of not dropping out of my usual syntax. (It was helpful to be reminded about `java.text`, for my next Scala Swing app.) – som-snytt Jun 14 '13 at 22:25
  • That's the first time I had use for a regex with an embedded ellipsis. – som-snytt Jun 14 '13 at 22:51

3 Answers3

4

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

maba
  • 47,113
  • 10
  • 108
  • 118
3

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  "
AmazingDreams
  • 3,136
  • 2
  • 22
  • 32
Randall Schulz
  • 26,420
  • 4
  • 61
  • 81
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.

som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • Ok, thanks. But I think it's a bit too much for an occasional drop in :) Good to know about `Formattable`, though. – 0__ Jun 16 '13 at 09:52