Video Coming Soon...

Created by Zed A. Shaw Updated 2025-11-14 00:08:54

36: numfmt

The key to solving numfmt is to understand that it's replicating an international standard for displaying numbers that is absolutely insane. Don't try to make sense of it. Just assume someone who probably says things like, "Half past two quarters near 3pm" also decided that 1,200,000 would 1.2M but that 10,001,000 would be 11M.

A quick hint is you can get the magnitude of a number with:

mag := math.Floor(math.Log10(number))

The Challenge

You are required to implement the SI style of number output up to, but not including billions. Everything above billions will be printed out exactly with besos at the end. If you want you can go as far as you want with this, but the pattern gets stranger the larger you get.

Requirements

Before you peek at my packages, try looking at strconv and math.

See the list of requirements

Spoilers

My code isn't quite exactly the same, but it's close enough. I'd say the biggest new thing to learn here is the use of fmt.Sprintf and the use of the % format style found in C languages. Before you peek at my solution, try reading the percent style formatting and the fmt.Scanf example.

See my first version codeView Source file go-coreutils/numfmt/main.go Only

package main

import (
  "fmt"
  "flag"
  "strconv"
  "log"
  "math"
  "bufio"
  "os"
)

type Opts struct {
  From string
  To string
  Numbers []string
}

func ParseOpts() Opts {
  var opts Opts

  flag.StringVar(&opts.From, "from", "", "Convert from")
  flag.StringVar(&opts.To, "to", "", "Convert to")
  flag.Parse()

  opts.Numbers = flag.Args()

  return opts
}

func to_si(num string) string {
  number, err := strconv.ParseFloat(num, 64)
  if err != nil { log.Fatal("that's not a number") }
  mag := math.Floor(math.Log10(number))

  switch {
  case mag < 3:
    return num
  case mag == 3:
    // need to separate k from hundres
    as_k := math.Floor(float64(number) / 1000.0)
    mod := math.Ceil(float64(int(number) % 1000))
    return fmt.Sprintf("%d.%dk", int(as_k), int(mod))
  case mag > 3 && mag < 6:
    as_m := math.Ceil(float64(number) / 1000.0)
    return fmt.Sprintf("%dk", int(as_m))
  case mag == 6:
    // need to separate mil from k
    as_m := math.Floor(float64(number) / 1000000.0)
    mod := math.Ceil(float64(int(number) % 1000000) / 1000.0)
    return fmt.Sprintf("%d.%dM", int(as_m), int(mod))
  case mag > 6 && mag <= 9:
    as_m := math.Ceil(float64(number) / 1000000.0)
    return fmt.Sprintf("%dM", int(as_m))
  default:
    return fmt.Sprintf("%sbesos", num)
  }
}

func main() {
  opts := ParseOpts()

  if opts.From != "" {
    log.Fatal("you should implement this")
  }

  if len(opts.Numbers) == 0 {
    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
      num := scanner.Text()
      fmt.Println(to_si(num))
    }
  } else {
    for _, num := range opts.Numbers {
      fmt.Println(to_si(num))
    }
  }
}

Testing It

If you want to push your learning further then you can try to implement an automated test for this. I actually need to learn how to test utilities like this with Go, so for now just consider this an extra challenge for later until I learn how to teach it.

Previous Lesson Next Lesson

Register for Learn Go the Hard Way

Register today for the course and get the all currently available videos and lessons, plus all future modules for no extra charge.