web-dev-qa-db-ja.com

golangでfloat64型を特定の精度に切り捨てるにはどうすればよいですか?

package main

import (
    "fmt"
    "strconv"
    )

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

Go float64変数の精度を2に下げるには、上記のプログラムを作成する必要がありました。この場合、strconvとfmtの両方を使用していました。それを行うことができる他の論理的な方法はありますか?

47
George Thomas

余分なコードは必要ありません...

import (
    "fmt"
)

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

テストコード

60
Baba

私はGoを始めたばかりで、あなたが望んでいることを達成する「toFixed」機能(JavaScriptのような)がなく、「ラウンド」機能さえも持たないことに驚きました。

他の場所から1行のラウンド関数を選択し、round()に依存するtoFixed()も作成しました。

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
}

使用法:

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)
60
David Calhoun

私は本当にその点を見ていませんが、strconvなしでこのようなことをすることができます:

package main

import (
    "fmt"
)

func main() {
    untruncated := 10 / 3.0
    truncated := float64(int(untruncated * 100)) / 100
    fmt.Println(untruncated, truncated)
}
17
creack

math/bigの使用について誰も言及していません。元の質問に関する結果は受け入れられた答えと同じですが、ある程度の精度($ money $)を必要とするfloatを使用している場合は、big.Floatを使用する必要があります。

元の質問ごと:

package main

import (
    "math/big"
    "fmt"
)

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

残念ながら、.Textは定義された丸めモードを使用しないことがわかります(そうでない場合は、この答えの方が便利かもしれません)。

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

// out -> 0.04

それでも、フロートをbig.Floatとして保存することには特定の利点があります。

6
threeve

最も簡単な解決策は、数値の切り捨てです(iがfloatであり、小数点以下2桁の精度が必要だと仮定します):

float64(int(i * 100)) / 100

例えば:

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

気をつけて!

大きな数値(最大値の境界を超えることができる数値)を扱っている場合は、上記が 深刻な浮動小数点精度の問題 につながる可能性があることを知っておく必要があります。

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

プリント:9.223372036854776e+18 -9.223372036854776e+17

こちらもご覧ください:

  1. 2ビット浮動小数点数の仕組み
  2. 64ビット浮動小数点数の仕組み
  3. golang math数値範囲定数
  4. golang math/big
4
Brenden

answer threeveが この問題 GitHubで私にもたらしたのは、値を丸めるための_math/big_に基づくソリューションが提示されている-このように丸め方法が正しく使用されている:

_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)
}
_

Threeveの例では、丸めモードも考慮されます。

_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)
_

_->_は正しく_0.05_を生成します

また、Go 1.10がリリースされ、 math.Round() 関数が追加されました。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
}
_

ただし、金銭の値を保存するためにfloatを使用しないでください。 (参照: 通貨を表すためにDoubleまたはFloatを使用しない理由 )これを回避する方法の1つは、 https://github.com/のようなdecimalを実装するライブラリを使用することですericlagergren/decimal または https://github.com/shopspring/decimal

3
Tobi

これは、型変換を使用してfloatを前後にintに変換する方法です。

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-2

1
mileusna

切り捨てと丸めはまったく異なる結果をもたらす可能性があるため、経度と緯度の浮動小数点数に注意してください。

1
Kugutsumen