常数1截断为整数?

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

Constant 1 truncated to integer?

问题

为什么这段代码无法编译?

package main
const a = 1.000001
const base = 0
const b = a+base
func main() {
    f(b)
}
func f(int) {}

它说1被截断了?还是说1不能被截断?它指的是哪个1?

有人回答说上述代码无法编译是因为bfloat64类型。但是为什么下面的代码可以编译呢?

package main
import "fmt"
const a = 1.000001
const b = a-0.000001
func main() {
    fmt.Printf("%T %v\n",a,a)
    fmt.Printf("%T %v\n",b,b)
    f(b)
}
func f(int) {}

在这里,bfloat64类型,但它可以传递给f函数。

英文:

Why wont this code compile?

package main
const a = 1.000001
const base = 0
const b = a+base
func main() {
    f(b)
}
func f(int) {}

<!

$ go run a.go
# command-line-arguments
./a.go:4: constant 1 truncated to integer

It's saying that 1 is truncated? Or that 1 cannot be truncated? Which 1 is it talking about?

Someone answered the above code doesn't compile because b is a float64. But then why does this compile:

package main
import &quot;fmt&quot;
const a = 1.000001
const b = a-0.000001
func main() {
    fmt.Printf(&quot;%T %v\n&quot;,a,a)
    fmt.Printf(&quot;%T %v\n&quot;,b,b)
    f(b)
}
func f(int) {}

<!

$ go run a.go 
float64 1.000001
float64 1

? b is a float64 here, but it can be passed to f.

答案1

得分: 7

Go团队最近发表了一篇关于这个问题的博文,我建议你阅读一下。

从介绍中可以得知:

> Go是一种静态类型语言,不允许混合使用数值类型的操作。你不能将float64加到int上,甚至不能将int32加到int上。然而,写1e6*time.Second、math.Exp(1)甚至1<<('\t'+2.0)是合法的。在Go中,常量与变量不同,它们的行为几乎像普通的数字。这篇文章解释了其中的原因以及它的含义。

简而言之,Go中的常量是无类型的。它们的类型只在最后一刻确定。

这解释了你上面的问题。给定以下代码:

func f(int) {}

然后:

f(1) // 正常
f(1.000) // 正常
f(1.0E6) // 正常
f(1.0001) // 错误
英文:

The go team made a blog post about this recently which I suggest you read.

From the introduction

> Go is a statically typed language that does not permit operations that
> mix numeric types. You can't add a float64 to an int, or even an int32
> to an int. Yet it is legal to write 1e6*time.Second or math.Exp(1) or
> even 1<<('\t'+2.0). In Go, constants, unlike variables, behave pretty
> much like regular numbers. This post explains why that is and what it
> means.

TLDR - constants are untyped in Go. Their type is only crystallized at the last moment.

That explains your problem above. Given

func f(int) {}

Then

f(1) // ok
f(1.000) // OK
f(1.0E6) // OK
f(1.0001) // BAD

答案2

得分: 4

Go对常量有非常严格的转换规则:

  • 如果常量值x可以用类型T的值表示,则可以在以下任何情况下将x转换为类型T。
  • 如果x可以由类型T的值表示,则可以将x转换为类型T。
  • 如果x是浮点常量,T是浮点类型,并且x经过使用IEEE 754舍入到最近偶数规则舍入后可以由类型T的值表示,则常量T(x)是舍入后的值。
  • 如果x是整数常量,T是字符串类型,则适用于非常量x的规则也适用于此情况。

了解这一点可能会对关于常量的golang博客文章有所帮助。
由于严格性,任何违反上述规则的转换都被视为错误。这样做的原因是Go试图尽可能准确地表示常量。这也意味着最终的类型是根据所使用表达式的上下文决定的。丢弃精度会破坏这一点,并且可能是一个可能的编程错误的标志。

如果你真的想将一个值四舍五入为整数,请将其转换为变量(在playground上的示例):

const b = 1.01
c := b
f(int(c))

这样做是因为编译器不会跟踪值的来源,并且常量规则不适用于变量。

但是为什么当我将它改为这样时它能工作?const a = 1.000001;const b = a-0.000001

在这个例子中,b等于1。1可以表示为整数,因此没有舍入和信息丢失。因此,这不是一个错误,因为它符合浮点值的转换规则(如前所述)。

英文:

Go has very strict conversion rules for constants:

> A constant value x can be converted to type T in any of these cases:
>
> - x is representable by a value of type T.
> - x is a floating-point constant, T is a floating-point type, and x is representable by a value of type T after rounding using IEEE 754
> round-to-even rules. The constant T(x) is the rounded value.
> - x is an integer constant and T is a string type. The same rule as for non-constant x applies in this case.

The golang blog post about constants may be helpful in understanding this further.
Due to the strictness every conversion that violates the quoted rules is considered an error. The reasoning behind this is that Go attempts to represent constants as accurately as possible. This also means that the final type is decided in the context of the used expression. Throwing away the precision defeats this and is a sign for a possible programming error.

If you really want to round a value to an integer, convert it to a variable (Example on play):

const b = 1.01
c := b
f(int(c))

This works since the compiler does not track the origin of the value and constant rules to not apply to variables.

But then why does it work when I change it to this? const a = 1.000001;const b = a-0.000001

In this example b equals 1. 1 can be represented as an integer, so no rounding and information loss is involved. Therefore this is not an error since it complies with the conversion rules for float values (as quoted before).

答案3

得分: 2

你的第一个程序可以这样重写:

package main

func main() {
    f(1.000001)
}

func f(int) {}

很明显,这里并没有将一个浮点数值传递给整数函数。

你的第二个程序可以类似地重写如下:

package main

import "fmt"

func main() {
    fmt.Printf("%T %v\n", 1.000001, 1.000001)
    fmt.Printf("%T %v\n", 1, 1)
    f(1)
}

func f(int) {}

看起来没问题。

我只是手动替换了ab这两个常量。这就是 Go 语言所做的。

英文:

Your first program can be rewritten like this:

package main
func main() {
    f(1.000001)
}
func f(int) {}

Which is clearly not passing an integer value to an integer function.

Your second program can similarly be rewritten like this:

package main
import &quot;fmt&quot;
func main() {
    fmt.Printf(&quot;%T %v\n&quot;,1.000001,1.000001)
    fmt.Printf(&quot;%T %v\n&quot;,1,1)
    f(1)
}
func f(int) {}

Which looks fine.

All I did was manually substitute the a and b constants. This is all go does.

答案4

得分: 1

免责声明:我没有使用Go的经验,但下面的答案基于与数据类型相关的一般原则。

你的函数f接受一个int类型的输入参数,但是根据你的代码,实际传递给它的值b是一个浮点数值。这将导致将浮点数值截断为整数值,正如错误消息所述。

我认为你可以通过将函数签名更改为接受浮点类型的值作为输入参数来解决这个问题,例如:

func f(float64) {}

Go示例

为了将其与我熟悉的一种语言(C#)进行比较,你可以看下面的代码:

public static void Main(string[] args)
{
    var a = 1.3;
    var b = 1.3 + 9;
    f(b);
    Console.WriteLine("Hello, world!");
}

public static void f(int a)
{
}

使用var关键字,我们不需要显式地将ab声明为double类型的变量。然而,因为浮点数值被赋给它们,它们的类型被推断为double。现在,如果你定义了一个以int类型的输入参数的方法f,然后传入ab,它将给你一个错误。但是,如果你将该方法更改为接受double值而不是int,你的代码将可以编译而不会出现问题。

C#示例

英文:

Disclaimer: I do not have any experience with Go, but the below answer is based on general principles related to data types.

Your function f takes an input parameter of type int, but the actual value that you pass to it i.e. b has a floating point value based on your code. That will cause truncation of the floating point value to an integer value, as the error message states.

I believe you could fix this issue by changing your function signature to take a floating type value as the input parameter i.e.

func f(float64) {}

Demo in Go

To compare this to a language I am familiar with (C#), you can look at the below code:

public static void Main(string[] args)
    {
        var a = 1.3;
        var b = 1.3 + 9;
        f(b);
        Console.WriteLine(&quot;Hello, world!&quot;);
    }
    
public static void f(int a)
    {
    }

Using the var keyword, we do not explicitly make a and b variables of datatype double. However, because floating-point values are being assigned to them, their type is inferred to be double. Now if you define the method f as taking an input parameter of datatype int and then pass in a or b. it will give you an error. However, if you change the method to take a double value instead of int, your code will compile without issue.

Demo in C#

huangapple
  • 本文由 发表于 2014年8月31日 00:07:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/25584329.html
匿名

发表评论

匿名网友

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

确定