Константа 1 усекается до целого числа?
Почему этот код не компилируется?
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
Это говорит о том, что 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) {}
$ go run a.go
float64 1.000001
float64 1
? b
это float64
здесь, но это может быть передано f
,
4 ответа
Команда go недавно написала об этом в блоге, который я предлагаю вам прочитать.
От введения
Go - это статически типизированный язык, который не допускает операций, которые смешивают числовые типы. Вы не можете добавить float64 к int или даже к int32 к int. Тем не менее, допустимо писать 1e6*time.Second или math.Exp(1) или даже 1<< ('\ t' + 2.0). В Go константы, в отличие от переменных, ведут себя почти как обычные числа. Этот пост объясняет, почему это так и что это значит.
TLDR - константы нетипизированы в Go. Их тип кристаллизуется только в последний момент.
Это объясняет вашу проблему выше. Дано
func f(int) {}
затем
f(1) // ok
f(1.000) // OK
f(1.0E6) // OK
f(1.0001) // BAD
В Go очень строгие правила преобразования констант:
Постоянное значение x может быть преобразовано в тип T в любом из этих случаев:
x
представимо значением типаT
,x
является константой с плавающей запятой, T является типом с плавающей запятой, и x представлен значением типаT
после округления с использованием правил округления до четности IEEE 754. ПостояннаяT(x)
это округленное значение.x
является целочисленной константой иT
это тип строки То же правило, что и для непостоянныхx
применяется в этом случае.
Сообщение в блоге golang о константах может быть полезно для дальнейшего понимания. Из-за строгости каждое преобразование, которое нарушает указанные правила, считается ошибкой. Это объясняется тем, что Go пытается представить константы с максимально возможной точностью. Это также означает, что окончательный тип определяется в контексте используемого выражения. Отбрасывание точности опровергает это и является признаком возможной ошибки программирования.
Если вы действительно хотите округлить значение до целого числа, преобразуйте его в переменную ( пример в игре):
const b = 1.01
c := b
f(int(c))
Это работает, поскольку компилятор не отслеживает происхождение значения и правила констант, которые не применяются к переменным.
Но тогда почему это работает, когда я изменяю это на это? const a = 1.000001;const b = a-0.000001
В этом примере b
равно 1. 1 может быть представлено как целое число, поэтому округления и потери информации не происходит. Следовательно, это не ошибка, поскольку она соответствует правилам преобразования значений с плавающей запятой (как указано выше).
Ваша первая программа может быть переписана так:
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, но приведенный ниже ответ основан на общих принципах, касающихся типов данных.
Ваша функция 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
, Теперь, если вы определите метод f
как принимая входной параметр типа данных int
а затем перейти в a
или же b
, это даст вам ошибку. Однако, если вы измените метод, чтобы взять double
значение вместо int
Ваш код будет скомпилирован без проблем.