英文:
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?
有人回答说上述代码无法编译是因为b
是float64
类型。但是为什么下面的代码可以编译呢?
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) {}
在这里,b
是float64
类型,但它可以传递给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 "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) {}
<!
$ 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) {}
看起来没问题。
我只是手动替换了a
和b
这两个常量。这就是 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 "fmt"
func main() {
fmt.Printf("%T %v\n",1.000001,1.000001)
fmt.Printf("%T %v\n",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) {}
为了将其与我熟悉的一种语言(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
关键字,我们不需要显式地将a
和b
声明为double
类型的变量。然而,因为浮点数值被赋给它们,它们的类型被推断为double
。现在,如果你定义了一个以int
类型的输入参数的方法f
,然后传入a
或b
,它将给你一个错误。但是,如果你将该方法更改为接受double
值而不是int
,你的代码将可以编译而不会出现问题。
英文:
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) {}
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("Hello, world!");
}
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论