英文:
How can we truncate float64 type to a particular precision?
问题
以下是翻译好的代码:
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。是否有其他逻辑方法可以实现这个目的?
英文:
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?
答案1
得分: 99
以下代码适用于许多简单的用例,涉及相对较小的数字和小精度输入。然而,由于数字超出float64范围以及IEEE-754舍入误差的问题,它可能无法适用于某些用例(其他语言也存在此问题)。
如果您关心使用更大的数字或需要更高的精度,以下代码可能无法满足您的需求,您应该使用一个辅助库(例如https://github.com/shopspring/decimal)。
我从其他地方找到了一个一行代码的round函数,并且还编写了依赖于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(四舍五入)
英文:
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)
答案2
得分: 86
你不需要任何额外的代码...它就是这么简单
import (
"fmt"
)
func main() {
k := 10 / 3.0
fmt.Printf("%.2f", k)
}
英文:
You don't need any extra code ... its as simple as
import (
"fmt"
)
func main() {
k := 10 / 3.0
fmt.Printf("%.2f", k)
}
答案3
得分: 31
我真的看不出来有什么意义,但你可以这样做而不使用strconv:
package main
import (
"fmt"
)
func main() {
untruncated := 10 / 3.0
truncated := float64(int(untruncated * 100)) / 100
fmt.Println(untruncated, truncated)
}
这段代码的作用是将10除以3,得到一个未截断的浮点数untruncated,然后将untruncated乘以100并转换为整数,再除以100转换回浮点数,得到截断两位小数的浮点数truncated。最后通过fmt.Println打印出untruncated和truncated的值。
英文:
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)
}
答案4
得分: 17
最简单的解决方案是数值截断(假设i
是一个浮点数,你想要保留2位小数):
float64(int(i * 100)) / 100
例如:
i := 123456.789
x := float64(int(i * 100)) / 100
// x = 123456.78
注意!
如果你处理的是大数(可能超过最大值范围),你应该知道上述方法可能会导致严重的浮点数精度问题(参考链接:https://play.golang.org/p/X2tNXihu_uN):
i := float64(1<<63) // 9223372036854775808.0
fmt.Println(i, float64(int64(i * 10)) / 10)
输出结果为:9.223372036854776e+18 -9.223372036854776e+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:
答案5
得分: 13
没有人提到使用math/big
。就原始问题而言,结果与被接受的答案相同,但如果你使用需要一定精度(比如货币)的浮点数,那么你应该使用big.Float
。
根据原始问题:
package main
import (
"math/big"
"fmt"
)
func main() {
// 原始问题
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))
// 输出 -> 0.04
尽管如此,将浮点数存储为big.Float
仍然有一定的优势。
英文:
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
.
答案6
得分: 10
没有问题,以下是代码的中文翻译:
没有检查大浮点数的函数
// 四舍五入,例如 12.3416 -> 12.35
func RoundUp(val float64, precision int) float64 {
return math.Ceil(val*(math.Pow10(precision))) / math.Pow10(precision)
}
// 向下取整,例如 12.3496 -> 12.34
func RoundDown(val float64, precision int) float64 {
return math.Floor(val*(math.Pow10(precision))) / math.Pow10(precision)
}
// 四舍五入到最近的数,例如 12.3456 -> 12.35
func Round(val float64, precision int) float64 {
return math.Round(val*(math.Pow10(precision))) / math.Pow10(precision)
}
英文:
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)
}
答案7
得分: 8
这个答案 由 threeve 提供,将我带到了 GitHub 上的 这个问题,其中介绍了一种基于 math/big
的解决方案来进行数值舍入 - 这样可以正确使用舍入方法:
package main
import (
"fmt"
"math/big"
)
func main() {
f := new(big.Float).SetMode(big.ToNearestEven).SetFloat64(10/3.0)
// 使用格式化将结果舍入到2位小数。
f.SetPrec(8)
fmt.Printf("%.2f\n", f)
}
在 threeve 的示例中,舍入模式也得到了尊重:
j := 0.045
f := new(big.Float).SetMode(big.AwayFromZero).SetFloat64(j)
// 使用格式化将结果舍入到2位小数。
f.SetPrec(8)
fmt.Printf("%.2f\n", f)
->
正确输出 0.05
此外,Go 1.10 已经发布,并添加了 math.Round()
函数,可以参考 icza 的优秀答案:https://stackoverflow.com/questions/39544571/golang-round-to-nearest-0-05/39544897#39544897
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
来存储货币值。(参考:https://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency)
解决这个问题的一种方法是使用实现了 decimal
的库,比如 https://github.com/ericlagergren/decimal 或 https://github.com/shopspring/decimal。
英文:
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: https://stackoverflow.com/questions/39544571/golang-round-to-nearest-0-05/39544897#39544897
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: https://stackoverflow.com/questions/3730019/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
答案8
得分: 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.
答案9
得分: 2
这是一个小技巧,可以通过类型转换将浮点数四舍五入:
package main
import (
"fmt"
)
func main() {
k := 10 / 3.0
k = float64(int(k*100)) / 100
fmt.Println(k) // 输出 3.33
}
你可以在这个链接上运行这段代码:https://play.golang.org/p/yg2QYcZ-2u
英文:
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
}
答案10
得分: 1
func FloatPrecision(num float64, precision int) float64 {
p := math.Pow10(precision)
value := float64(int(num*p)) / p
return value
}
英文:
func FloatPrecision(num float64, precision int) float64 {
p := math.Pow10(precision)
value := float64(int(num*p)) / p
return value
}
答案11
得分: 0
所以在经过多次搜索后,我终于找到了一个非常简单的解决方案,可以让你在没有任何奇怪的数学运算的情况下轻松控制浮点数的精度!
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
来源:https://godoc.org/github.com/shopspring/decimal#Decimal.Round
虽然我喜欢一些简单的方法,比如 "%.3f\n", k
,但这些方法会产生一个字符串,然后我还需要用另一个 strconv 命令将其转换回浮点数,而我不想这样做。
英文:
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.
答案12
得分: 0
将数字保留到小数点后一位:
fmt.Printf("%.1f", n)
<https://go.dev/play/p/eavHNPXhZK6>
英文:
Round to the 1st decimal
fmt.Printf("%.1f", n)
<https://go.dev/play/p/eavHNPXhZK6>
答案13
得分: 0
为了避免舍入误差和溢出,我们可以进行以下操作:
- 首先,使用
fmt.Sprintf
将float64
转换为string
。 - 然后,使用
strconv.ParseFloat
将字符串转换为浮点数。
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
英文:
To avoid rounding issues and overflows, we may do -
- First, convert
float64
tostring
usingfmt.Sprintf
- 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)
}
答案14
得分: -4
修改自@creack
package main
import (
"fmt"
)
func main() {
//未截断的值 := 10/3.0
未截断的值 := 4.565
临时值 := int(未截断的值*100)
小数点后一位 := int(未截断的值*1000)-临时值*10
if 小数点后一位>=5{
临时值 += 1
}
截断后的值 := float64(临时值)/100
fmt.Println(未截断的值, 截断后的值)
}
英文:
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)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论