使用math.Pow进行四舍五入到最接近千位的计算时,结果会有所不同。

huangapple go评论76阅读模式
英文:

Different results while rounding to nearest thousand using math.Pow

问题

我试图将一个数字四舍五入到最接近的千位数,但是当我使用math.Pow时,通常会得到不同的结果。示例:

fmt.Println(math.Pow(10., 3.))
x := math.Pow(10, 3)
y := (((3251 - 1000) / x) * x)
fmt.Println(y)

输出:1000
输出:2251

GoPlayGround

当我使用1000而不是math.Pow(10., 3.)时,我得到了我想要的结果:

y := (((3251 - 1000) / 1000) * 1000)
fmt.Println(y)

输出:2000

Go PlayGround

我做错了什么?我希望能得到一些帮助。

英文:

I was trying to round a number to the nearest thousand but I usually get a different result when I use math.Pow. Examples:

fmt.Println(math.Pow(10., 3.))
x := math.Pow(10, 3)
y := (((3251 - 1000) / x) * x)
fmt.Println(y)

Output: 1000
Output: 2251

GoPlayGround

When I use 1000 instead math.Pow(10., 3.) I get what I want:

y := (((3251 - 1000) / 1000) * 1000)
fmt.Println(y)

Output: 2000

Go PlayGround

What am I doing wrong? I would appreciate some help.

答案1

得分: 3

表达式 y := ((3251 - 1000) / 1000) * 1000 是一个常量表达式,即只有常量无类型字面操作数,并且在编译时计算。特别是:

> 如果二元操作(除移位操作外)的无类型操作数属于不同的种类,则结果是出现在此列表中较后的操作数的种类。

最后的操作数 1000(除法和乘法都是如此)是一个无类型 int,因此除法的结果也是一个 int,并且按整数精度截断,正如你所期望的那样:

    // (3251 - 1000) -> int 2251
    // (3251 - 1000) / 1000 -> int 2
    // ((3251 - 1000) / 1000) * 1000 -> int 2000

    y := ((3251 - 1000) / 1000) * 1000
	fmt.Println(reflect.TypeOf(y)) // int

使用 math.Pow 替代后,表达式不再是常量(它是函数调用的结果),现在你有一个由 Pow 的返回类型确定的有类型 float64 变量:

    // (3251 - 1000) -> 2251 int
    // (3251 - 1000) / x -> 2.251 float64
    // ((3251 - 1000) / x) * x -> 2251 float64

    y := (((3251 - 1000) / x) * x)
	fmt.Println(reflect.TypeOf(y)) // float64

因此,在后一种情况下,除法得到的小数部分被保留,并在乘以后再次得到。

Playground: https://play.golang.org/p/v_mX3mnM6tT

<hr>

要四舍五入到最接近的千位数,你可以使用 @icza这个答案中提到的技巧:

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

func main() {
    x := Round(3251-1000, 1000.)
    fmt.Println(x) // 2000
}
英文:

The expression y := ((3251 - 1000) / 1000) * 1000 is a constant expression, i.e. has only constant untyped literal operands, and it's evaluated at compile time. In particular:

> If the untyped operands of a binary operation (other than a shift) are of different kinds, the result is of the operand's kind that appears later in this list

The last operands 1000 (of both the division and multiplication) are an untyped int, therefore the result of the division is also an int, and truncated to integer precision as you were expecting:

    // (3251 - 1000) -&gt; int 2251
    // (3251 - 1000) / 1000 -&gt; int 2
    // ((3251 - 1000) / 1000) * 1000 -&gt; int 2000

    y := ((3251 - 1000) / 1000) * 1000
	fmt.Println(reflect.TypeOf(y)) // int

With math.Pow instead the expression is not constant anymore (it's the result of a function call), and now you have a typed float64 variable resulting from the return type of Pow:

    // (3251 - 1000) -&gt; 2251 int
    // (3251 - 1000) / x -&gt; 2.251 float64
    // ((3251 - 1000) / x) * x -&gt; 2251 float64

    y := (((3251 - 1000) / x) * x)
	fmt.Println(reflect.TypeOf(y)) // float64

So in the latter case, the decimal that results from the division is preserved and you get it back after you multiply it again.

Playground: https://play.golang.org/p/v_mX3mnM6tT

<hr>

To round to the nearest thousand you can use the trick suggested by @icza in this answer:

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

func main() {
    x := Round(3251-1000, 1000.)
    fmt.Println(x) // 2000
}

答案2

得分: 0

根据建议:

package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Println(math.Pow(10, 3))
	x := math.Pow(10, 3)
	y := (((3251 - 1000) / x) * x)
	fmt.Println(y)
	fmt.Println(Round(y, 1000))
}
func Round(x, unit float64) float64 {
	return math.Round(x/unit) * unit
}

输出:

1000
2251
2000
英文:

Based on the suggestions:

package main

import (
	&quot;fmt&quot;
	&quot;math&quot;
)

func main() {
	fmt.Println(math.Pow(10, 3))
	x := math.Pow(10, 3)
	y := (((3251 - 1000) / x) * x)
	fmt.Println(y)
	fmt.Println(Round(y, 1000))
}
func Round(x, unit float64) float64 {
	return math.Round(x/unit) * unit
}

Output:

1000
2251
2000

huangapple
  • 本文由 发表于 2021年6月23日 17:26:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/68097129.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定