意外的循环与无效的浮点数

I'm working through the Coursera Go course ... it's pretty good.

In my code below, it works fine when the user correctly a float. But when they enter some random string, it prints the error message and prompt again for each character. Why is that?

package main

import "fmt"

func readFloat(title string) float64 {
  var userInput float64

  for {
    fmt.Println("Please enter a float: ")
    _, err := fmt.Scanf("%f", &userInput)

    if err != nil {
      fmt.Printf("Wooops! That's not a float
")
    } else {
      return userInput
    }
  }
}

func main() {
  var f float64

  f = readFloat("acceleration")
    fmt.Printf("You entered: %.04f
", f)
}
~/src/coursera/go/course-2-functions-methods/week2 $ go run so.go
Please enter a float:
33.3
You entered: 33.3000
~/src/coursera/go/course-2-functions-methods/week2 $ go run so.go
Please enter a float:
sdf
Wooops! That's not a float
Please enter a float:
Wooops! That's not a float
Please enter a float:
Wooops! That's not a float
Please enter a float:
Wooops! That's not a float
Please enter a float:

The problem is that if fmt.Scanf() encounters an invalid input, it will stop consuming it. E.g. you want to parse a float using %f, but if user inputs sdf, fmt.Scanf() will know it's invalid after the first s character, so returns an error and will not consume the rest.

So the next loop iteration begins, it consumes the second character d, which is again invalid etc.

If you try to enter s2 for example, this will become clear:

Please enter a float: 
s2
Wooops! That's not a float
Please enter a float: 
You entered: 2.0000

First s char is invalid, but the next iteration will parse 2.

One way to "fix" this is to read a whole line as a string, then attempt to parse a float out of the string. For reading a line, use bufio.Scanner, for parsing a float, use strconv.ParseFloat().

Here's an example how you can do that:

func readFloat(title string) float64 {
    scanner := bufio.NewScanner(os.Stdin)
    for {
        fmt.Printf("Please enter a float for %s: ", title)
        if !scanner.Scan() {
            fmt.Println("Error readling line")
            return 0 // Should return an error too!
        }

        line := scanner.Text()
        userInput, err := strconv.ParseFloat(line, 64)
        if err == nil {
            return userInput
        }
        fmt.Printf("Wooops! That's not a float
")
    }
}

That's because your for loop is an infinite loop if user enters a wrong value. You should have a return statement after your fmt.Printf("you entered wrong number"), so that the for loop can exit.