85
package main

import (
    "fmt"
    "strconv"
    )

func main() {
    k := 10/3.0
    i := fmt.Sprintf("%.2f", k)
    f,_ := strconv.ParseFloat(i, 2)
    fmt.Println(f)
}

I had to write the program above to decrease the precision of a go float64 variable to 2. In this case I was using both strconv and fmt. Is there some other logical method by which it can be done?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
George Thomas
  • 1,246
  • 2
  • 11
  • 14
  • 2
    While displaying the data in html, I did not want to display or store more than 2 digits. I was facing the situation where I had to convert it into string so was thinking of alternative methods. – George Thomas Aug 23 '13 at 18:27
  • 11
    Just using fmt.Sprintf("%.2f", k) when you show it in the html is probably the better way to this. – Jeremy Wall Aug 23 '13 at 19:08
  • 4
    I have the same need, but for a different reason. I am taking a total money value and dividing it over X days. I need each day to have an actual money value to the nearest penny, not a fraction of a penny, because I can't charge my customer for half a penny. I don't want to actually store a more specific value because I also need to take the total of my X money values, add them back up, and find the difference from the original amount, so that I can add that difference on to the last day's charge. – Blair Connolly May 21 '14 at 12:21
  • On top of the examples below, instead of truncating using `int()` the math library has `Trunc(float64) float64` that returns just the integer type, it's better to use it. – Not_a_Golfer Jun 15 '15 at 12:34
  • Blair Connolly - do not use float to store monetary values! https://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency – Tobi Mar 09 '18 at 15:09
  • If someone looking for all decimal points need to be removed, conversions can be used: i := int(k) – Hem Sep 18 '19 at 23:04

15 Answers15

99

The following code should work for a lot of simple use cases with relatively small numbers and small precision inputs. However, it may not work for some uses cases because of numbers overflowing out of the range of float64 numbers, as well as IEEE-754 rounding errors (other languages have this issue as well).

If you care about using larger numbers or need more precision, the following code may not work for your needs, and you should use a helper library (e.g. https://github.com/shopspring/decimal).

I picked up a one-liner round function from elsewhere, and also made toFixed() which depends on round():

func round(num float64) int {
    return int(num + math.Copysign(0.5, num))
}

func toFixed(num float64, precision int) float64 {
    output := math.Pow(10, float64(precision))
    return float64(round(num * output)) / output
}

Usage:

fmt.Println(toFixed(1.2345678, 0))  // 1
fmt.Println(toFixed(1.2345678, 1))  // 1.2
fmt.Println(toFixed(1.2345678, 2))  // 1.23
fmt.Println(toFixed(1.2345678, 3))  // 1.235 (rounded up)
David Calhoun
  • 8,315
  • 4
  • 30
  • 23
  • 15
    Probably down-voted because it makes slightly wrong answers in some cases, and really wrong answers in others; especially where high "precision" is requested. i.e. `toFixed(92234, 14)` -> "-92233.72036854776" – rvalue Feb 28 '17 at 12:32
  • unfortunately, I used it thinking it was updated...its still not fixed as of this comment. try 470994421302.9791 to a precision of 8 for example. – Mark Dec 09 '19 at 22:34
89

You don't need any extra code ... its as simple as

import (
    "fmt"
)

func main() {
    k := 10 / 3.0
    fmt.Printf("%.2f", k)
}

Test Code

Mahdi Yusuf
  • 19,931
  • 26
  • 72
  • 101
Baba
  • 94,024
  • 28
  • 166
  • 217
  • 14
    This isn't rounding, it's truncation. Also, it works as long as the result of the operation is meant to be printed, but additional operations would require to parse the number back into a float. Golang should really support rounding in the library. Best solution I have found is to use the decimal package. github.com/shopspring/decimal – Enmanuel Rivera Apr 19 '16 at 15:30
  • @EnmanuelRivera I can't think of a situation where you would want to perform an operation on a rounded float. I've added my answer using `math/big`, which allows you to output a string value to a specified precision at any time, while keeping the "exact" float intact. – trey-jones Jun 07 '16 at 03:31
  • 42
    I'm confused why this is accepted + upvoted? This is not truncation, it _will_ round numbers up, despite the comments above. e.g. `fmt.Printf("%.2f", 2.289)` will give you `2.29` (and just `%f` will show the full `2.289` - so this is not an issue of constants being rounded etc.) – David Roberts May 05 '17 at 09:07
  • 5
    Can confirm, this rounds up. – carbocation Jan 19 '18 at 02:22
  • 1
    I'm going to guess it's accepted and upvoted as it fits the need of many people and what they were looking for, including me and the OP, even if it doesn't fit the official definition of truncation. – squirtgun Jul 01 '20 at 18:58
31

I really don't see the point, but you could do something like this without strconv:

package main

import (
    "fmt"
)

func main() {
    untruncated := 10 / 3.0
    truncated := float64(int(untruncated * 100)) / 100
    fmt.Println(untruncated, truncated)
}
Matt
  • 22,721
  • 17
  • 71
  • 112
creack
  • 116,210
  • 12
  • 97
  • 73
  • 2
    +1. Fixed a little typo. Also here's a Go play: http://play.golang.org/p/RFy6cN148X – Matt Aug 23 '13 at 16:56
  • 1
    This is not the same thing, consider that convert to int won't round up.http://play.golang.org/p/ehMAFopHTX – Isaiah Jul 16 '15 at 13:49
  • 1
    Indeed, it does not round up, it truncates. If you want to round up, you could play with the math package. – creack Jul 16 '15 at 15:39
  • 1
    Or, You can round by modifying the expression to float64(int(untruncated * 100 + 0.5)) / 100 – runec Oct 05 '15 at 13:03
  • This won't work if we're using Printf, only works in Println. https://play.golang.org/p/fhje-IvhHts – prakashb Jul 23 '18 at 14:40
17

The simplest solution is numeric truncation (assuming i is a float and you want a precision of 2 decimal points):

float64(int(i * 100)) / 100

For example:

i := 123456.789
x := float64(int(i * 100)) / 100
// x = 123456.78

BEWARE!

If you're dealing with large numbers (numbers that can cross the max value boundaries), you should know that the above can lead to serious floating point accuracy issues:

i := float64(1<<63) // 9223372036854775808.0
fmt.Println(i, float64(int64(i * 10)) / 10)

Prints: 9.223372036854776e+18 -9.223372036854776e+17

See also:

  1. how 32 bit floating point numbers work
  2. how 64 bit floating point numbers work
  3. golang math numeric value range constants
  4. golang math/big
Brenden
  • 7,708
  • 11
  • 61
  • 75
13

No one has mentioned using math/big. The results as pertains to the original question are the same as the accepted answer, but if you are working with floats that require a degree of precision ($money$), then you should use big.Float.

Per the original question:

package main

import (
    "math/big"
    "fmt"
)

func main() {
    // original question
    k := 10 / 3.0
    fmt.Println(big.NewFloat(k).Text('f', 2))
}

Unfortunately, you can see that .Text does not use the defined rounding mode (otherwise this answer might be more useful), but rather always seems to round toward zero:

j := 0.045
fmt.Println(big.NewFloat(j).SetMode(big.AwayFromZero).Text('f', 2)

// out -> 0.04

Nevertheless, there are certain advantages to having your float stored as a big.Float.

trey-jones
  • 3,329
  • 1
  • 27
  • 35
  • 1
    How to use other rounding modes is described [in this issue on GitHub](https://github.com/golang/go/issues/18649#issuecomment-274994822): "If you need to round than do it before formatting and you get the desired result: https://play.golang.org/p/iSo7VROcPj " – Tobi Mar 08 '18 at 12:52
10

Functions without checking for large floats

// Rounds like 12.3416 -> 12.35
func RoundUp(val float64, precision int) float64 {
    return math.Ceil(val*(math.Pow10(precision))) / math.Pow10(precision)
}

// Rounds like 12.3496 -> 12.34
func RoundDown(val float64, precision int) float64 {
    return math.Floor(val*(math.Pow10(precision))) / math.Pow10(precision)
}

// Rounds to nearest like 12.3456 -> 12.35
func Round(val float64, precision int) float64 {
    return math.Round(val*(math.Pow10(precision))) / math.Pow10(precision)
}
WeizhongTu
  • 6,124
  • 4
  • 37
  • 51
Stremovskyy
  • 452
  • 7
  • 18
8

The answer by threeve brought me to this issue on GitHub where a solution based on math/big for rounding values is presented - this way the rounding method is used correctly:

package main

import (
    "fmt"
    "math/big"
)

func main() {
    f := new(big.Float).SetMode(big.ToNearestEven).SetFloat64(10/3.0)
    // Round to 2 digits using formatting.
    f.SetPrec(8)
    fmt.Printf("%.2f\n", f)
}

The rounding mode is also respected in threeve's example:

j := 0.045

f := new(big.Float).SetMode(big.AwayFromZero).SetFloat64(j)
// Round to 2 digits using formatting.
f.SetPrec(8)
fmt.Printf("%.2f\n", f)

-> correctly yields 0.05

Also, Go 1.10 has been released and added a math.Round() function, see this excellent answer by icza: Golang Round to Nearest 0.05

package main

import (
    "fmt"
    "math"
)

func main() {

    fmt.Println(Round(10/3.0, 0.01))

}

func Round(x, unit float64) float64 {
    return math.Round(x/unit) * unit
}

However, one should not use float for storing monetary values. (See: Why not use Double or Float to represent currency?) One way around this is using a library that implements decimal like https://github.com/ericlagergren/decimal or https://github.com/shopspring/decimal

Tobi
  • 904
  • 1
  • 8
  • 29
3

Be careful with longitude and latitude float as truncating and rounding can yield completely different results... as it can put you on the wrong side of a latitude boundary.

Kugutsumen
  • 878
  • 8
  • 18
2

This is a little workaround how can you round float using type conversion to int back and forth:

package main

import (
    "fmt"
)

func main() {
    k := 10 / 3.0
    k = float64(int(k*100)) / 100
    fmt.Println(k)  // output 3.33
}

https://play.golang.org/p/yg2QYcZ-2u

mileusna
  • 624
  • 6
  • 11
1
func FloatPrecision(num float64, precision int) float64 {
    p := math.Pow10(precision)
    value := float64(int(num*p)) / p
    return value
}
0

So after much hunting around, coming to this thread multiple times I finally found a very simple solution that allows you to control the precision of floats very simply without any weird math!

package main

import (
    "fmt"
    "github.com/shopspring/decimal"
)

func main() {
    k := 101.3874927181298723478
    p := 5

    v := decimal.NewFromFloat(k).Round(int32(p))
    fmt.Println(v)
}
// 101.38749

Source: https://godoc.org/github.com/shopspring/decimal#Decimal.Round

While I like some of the simple methods like "%.3f\n", k options, these produced a string that I would then have to convert back into a float with another strconv command that I didn't want to do.

Vallier
  • 551
  • 1
  • 6
  • 10
0

Round to the 1st decimal

fmt.Printf("%.1f", n)

https://go.dev/play/p/eavHNPXhZK6

Big_Boulard
  • 799
  • 1
  • 13
  • 28
0

To avoid rounding issues and overflows, we may do -

  1. First, convert float64 to string using fmt.Sprintf
  2. Then, use strconv.ParseFloat to convert the string to float
func adjustDecimals(num float64, precision int) (float64, error) {  
    formatString := fmt.Sprintf("%%.%df", precision)  
    strNum := fmt.Sprintf(formatString, num)  
    return strconv.ParseFloat(strNum, 64)  
}  

https://go.dev/play/p/cafkX_3Asw5

Robert Ranjan
  • 1,538
  • 3
  • 19
  • 17
0

Using the std lib, this solution provides three methods. The first two methods (TruncFloat and RoundFloat) meet the criteria:

  • Supports very large floats
  • Supports very small floats
  • Supports invalid floats
  • Truncates when you want it to
  • Rounds when you want it to

The third method TruncFloatFast provides more speed, but is limited and provides an error for bad rounds. For most of my applications I prefer robustness over speed, but when speed matters I want to know about bad rounds. I did a quick perf measure on my dev machine.

TruncFLoat: 155 millis per million calls
RoundFloat: 150 millis per million calls
RoundFloatFast: 3 millis per million calls
// prec controls the number of digits (excluding the exponent)
//  prec of -1 uses the smallest number of digits
func TruncFloat(f float64, prec int) (float64, error) {
    floatBits := 64

    if math.IsNaN(f) || math.IsInf(f, 1) || math.IsInf(f, -1) {
        return 0, fmt.Errorf("bad float val %f", f)
    }

    fTruncStr := strconv.FormatFloat(f, 'f', prec+1, floatBits)
    fTruncStr = fTruncStr[:len(fTruncStr)-1]
    fTrunc, err := strconv.ParseFloat(fTruncStr, floatBits)
    if err != nil {
        return 0, err
    }

    return fTrunc, nil
}

// prec controls the number of digits (excluding the exponent)
//  prec of -1 uses the smallest number of digits
func RoundFloat(f float64, prec int) (float64, error) {
    floatBits := 64

    if math.IsNaN(f) || math.IsInf(f, 1) || math.IsInf(f, -1) {
        return 0, fmt.Errorf("bad float val %f", f)
    }

    fRoundedStr := strconv.FormatFloat(f, 'f', prec, floatBits)
    fRounded, err := strconv.ParseFloat(fRoundedStr, floatBits)
    if err != nil {
        return 0, err
    }

    return fRounded, nil
}

func RoundFloatFast(f float64, prec int) (float64, error) {
    mul := math.Pow10(prec)
    if mul == 0 {
        return 0, nil
    }

    product := f * mul
    var roundingErr error
    if product > float64(math.MaxInt64) {
        roundingErr = fmt.Errorf("unsafe round: float64=%+v, places=%d", f, prec)
    }

    return math.Round(product) / mul, roundingErr
}

Test Examples

    f, _ := TruncFloat(0.000000000000000000000000000000019, 32)
    expected := 0.00000000000000000000000000000001
    if f != expected {
        t.Errorf("Expected=%+v Actual=%+v", expected, f)
    }

    f, _ = TruncFloat(2.289, 2)
    expected = 2.28
    if f != expected {
        t.Errorf("Expected=%+v Actual=%+v", expected, f)
    }

    f, _ = TruncFloat(111222333444555666777888999000111222333.123456789, 8)
    expected = 111222333444555666777888999000111222333.12345678
    if f != expected {
        t.Errorf("Expected=%+v Actual=%+v", expected, f)
    }

    f, _ = TruncFloat(1, 0)
    expected = 1.
    if f != expected {
        t.Errorf("Expected=%+v Actual=%+v", expected, f)
    }

    f, _ = TruncFloat(1, 2)
    expected = 1.
    if f != expected {
        t.Errorf("Expected=%+v Actual=%+v", expected, f)
    }

    f, _ = RoundFloat(0.000000000000000000000000000000011, 32)
    expected = 0.00000000000000000000000000000001
    if f != expected {
        t.Errorf("Expected=%+v Actual=%+v", expected, f)
    }

    f, _ = RoundFloat(92234, 14)
    expected = 92234.
    if f != expected {
        t.Errorf("Expected=%+v Actual=%+v", expected, f)
    }

    f, _ = RoundFloat(2.289, 2)
    expected = 2.29
    if f != expected {
        t.Errorf("Expected=%+v Actual=%+v", expected, f)
    }

    f, _ = RoundFloat(111222333444555666777888999000111222333.123456789, 8)
    expected = 111222333444555666777888999000111222333.12345679
    if f != expected {
        t.Errorf("Expected=%+v Actual=%+v", expected, f)
    }

    a := time.Now().UnixMilli()
    for i := 0.; i < 1_000_000; i+= 1.23456789 {
        TruncFloat(i, 2)
    }
    t.Logf("TruncFloat millis=%d", time.Now().UnixMilli()-a)
    
    a = time.Now().UnixMilli()
    for i := 0.; i < 1_000_000; i+= 1.23456789 {
        RoundFloat(i, 2)
    }
    t.Logf("RoundFloat millis=%d", time.Now().UnixMilli()-a)
    
    a = time.Now().UnixMilli()
    for i := 0.; i < 1_000_000; i+= 1.23456789 {
        RoundFloatFast(i, 2)
    }
    t.Logf("RoundFloatFast millis=%d", time.Now().UnixMilli()-a)
Josh Hibschman
  • 3,148
  • 1
  • 25
  • 27
-4

modify from @creack

package main

import (
    "fmt"
)

func main() {

    //untruncated := 10/3.0
    untruncated := 4.565
    tmp := int(untruncated*100)
    last := int(untruncated*1000)-tmp*10
    if last>=5{
        tmp += 1
    }
    truncated := float64(tmp)/100

    fmt.Println(untruncated, truncated)
}
justin
  • 3
  • 1