简体   繁体   中英

Constant 1 truncated to integer?

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 .

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

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

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.

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

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("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.

Demo in C#

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM