Confused with Type conversions in golang - go

I recently tried to learn golang. But I got confused with this code from https://tour.golang.org/basics/13.
package main
import (
"fmt"
"math"
)
func main() {
var x, y int = 3, 4
var f float64 = math.Sqrt(float64(x*x + y*y))
var z uint = uint(f)
fmt.Println(x, y, z)
}
That one works well. Then I tried
var f = math.Sqrt(9 + 16)
which also works. But when I change it to var f = math.Sqrt(x*x + y*y) why is it not working? It says cannot use x * x + y * y (type int) as type float64 in argument to math.Sqrt
I have javascript background, and I somehow can't understand the code above.

The math.Sqrt function signature:
func Sqrt(x float64) float64
requires that you pass float64
In this case:
var f float64 = math.Sqrt(float64(x*x + y*y))
You are converting to float64 directly
In this case:
var f = math.Sqrt(x*x + y*y)
you are passing an int, when float64 is required.
In this case:
var f = math.Sqrt(9 + 16)
The compiler is able to infer the type, and pass float64 for you.

But when we pass a number directly, it automatically converted?
No, not really *). Your "direct numbers" are called "constants" in Go and constants are often "untyped" and (almost) of arbitrary precision. There are special rules for constants: A constant 5 and the integer a defined by a := 5 behave differently because 5 is a constant with special rules and not an int.
Constant expressions like 9 + 16 are evaluated at compile time like if you had typed 25. This 25 is still a (constant.
While Go does not have automatic type conversions for types it does have automatic conversions from constants to several types. The constant 25 can be converted to float64 or int, uint8 or even complex128 automatically.
Please read the blog post https://blog.golang.org/constants and the official language spec for a full explanation and all details: https://golang.org/ref/spec#Constants . This explains the strange notion of
"untyped integer" better than I could.
*) "not really" because it is not helpful to think about it that way. The distinction of constants is special in Go: Most other languages tread 3+5 as a sum of two ints resulting in an int while Go sees two untyped integer constants and evaluates this expression into a new arbitrary precision, untyped constant. Only later are constants converted to actual integers.

Related

Printing type of the numeric constant causes overflow

I am new to Go and currently following A Tour of Go.
I am currently at page Numeric Constants. Down below is a trimmed down version of the code that runs on that page:
package main
import "fmt"
const Big = 1 << 100
func needFloat(x float64) float64 {
return x * 0.1
}
func main() {
fmt.Println(needFloat(Big))
// fmt.Printf("Type of Big %T", Big)
}
this code compiles successfully with the output 1.2676506002282295e+29
The following code however will not compile and give an error:
package main
import "fmt"
const Big = 1 << 100
func needFloat(x float64) float64 {
return x * 0.1
}
func main() {
fmt.Println(needFloat(Big))
fmt.Printf("Type of Big %T", Big)
}
Output:
./prog.go:9:13: constant 1267650600228229401496703205376 overflows int
Why do you think this happened? I hope you will kindly explain.
The constant Big is an untyped constant. An untyped constant can be arbitrarily large and it doesn't have to fit into any predefined type's limits. It is interpreted and truncated in the context it is used.
The function needFloat gets a float64 argument. At this instance Big is converted to a float64 and used that way.
When you use it for Printf, it tries to pass it in as an int because it is not a decimal number (otherwise it would've converted it to float64), and it causes an overflow. Pass it as float64(Big), and it should work.
I guess the reason is that Big gets computed (i.e. casted right before being passed to needFloat, and gets instead computed as a int64 before the Printf. As a proof, the following statement computes correctly:
package main
import "fmt"
const Big = 1 << 100
func main() {
fmt.Printf("Type of Big %T", float64(Big))
}
Hope this helps.
The untyped constant n must be converted to a type before it can be assigned to the interface{} parameter in the call to fmt.Println.
fmt.Println(a ...interface{})
When the type can’t be inferred from the context, an untyped constant is converted to a bool, int, float64, complex128, string or rune depending of the format of the constant.
In this case the constant is an integer, but n is larger than the maximum value of an int.
However, n can be represented as a float64.
const n = 9876543210 * 9876543210
fmt.Println(float64(n))
For exact representation of big numbers, the math/big package implements arbitrary-precision arithmetic. It supports signed integers, rational numbers and floating-point numbers.
This is taken from https://yourbasic.org/golang/gotcha-constant-overflows-int/.

confusion about convert `uint8` to `int8`

I want to convert uint8 to int, so I write a const 0xfc, and try to use int8(0xfc) to convert it. However the code raises an error:
package main
import (
"fmt"
)
func main() {
a := int8(0xfc) // compile error: constant 252 overflows int8
b := a
fmt.Println(b)
}
But if I defer the type conversion after assignment, the code can work around.
package main
import (
"fmt"
)
func main() {
a := 0xfc
b := int8(a) // ok
fmt.Println(b)
}
My question:
Is there any difference between these two codes?
Why does the first one raise a compile error?
see: https://golang.org/ref/spec#Constant_expressions
The values of typed constants must always be accurately representable by values of the constant type. The following constant expressions are illegal:
uint(-1) // -1 cannot be represented as a uint
int(3.14) // 3.14 cannot be represented as an int
int64(Huge) // 1267650600228229401496703205376 cannot be represented as an int64
Four * 300 // operand 300 cannot be represented as an int8 (type of Four)
Four * 100 // product 400 cannot be represented as an int8 (type of Four)
see:
https://blog.golang.org/constants
not all integer values can fit in all integer types. There are two problems that might arise: the value might be too large, or it might be a negative value being assigned to an unsigned integer type. For instance, int8 has range -128 through 127, so constants outside of that range can never be assigned to a variable of type int8:
var i8 int8 = 128 // Error: too large.
Similarly, uint8, also known as byte, has range 0 through 255, so a large or negative constant cannot be assigned to a uint8:
var u8 uint8 = -1 // Error: negative value.
This type-checking can catch mistakes like this one:
type Char byte
var c Char = '世' // Error: '世' has value 0x4e16, too large.
If the compiler complains about your use of a constant, it's likely a real bug like this.
My actual demand is to convert a byte to int32 when parsing a binary file. I may encounter the constant byte 0xfc, and should transfer it to the int8 before converting it to the int32 with the consideration of sign.
Yes, this is the way to go:
var b byte = 0xff
i32 := int32(int8(b))
fmt.Println(i32) // -1
Is there any difference between these two codes?
The first example uses a constant expression. The second uses plain expressions. Constant expressions are evaluated at compile time with different rules from plain expressions.
Why does the first one raise a compile error?
The int8(0xfc) is a typed constant expression. Values of typed constants must always be accurately representable by values of the constant type. The compiler reports an error because the value 252 cannot be represented by the values of int8.
Based on comments on other answers, I see that the goal is to get an int32 from a byte with sign extension. Given a byte variable b, use the expression int32(int8(b)) to get the int32 value with sign extension.

go float zero division compiler error

How does this behaviour sense? wouln't it make mor sense to just print an compiler warning instead of an error?
func main() {
var y float64 = 0.0
var x float64 = 4.0 / y
fmt.Println(x)
}
+Inf
func main() {
var x float64 = 4.0 / 0.0
fmt.Println(x)
}
prog.go:9:22: division by zero
Golang numeric consts are special. They're not directly mapped to any IEEE754 float type, and they're not able to store infinities or -0 for example.
From the documentation:
Numeric constants represent exact values of arbitrary precision and do not overflow. Consequently, there are no constants denoting the IEEE-754 negative zero, infinity, and not-a-number values.
This choice brings some power, as it reduces overflowing in constants:
var x float64 = 1e1000 / 1e999 // yes, this is 10
If you need an infinity value you may do
var x float64 = math.Inf(1)

Can I make type redefinition more optimal?

I have such code:
type Speed float64
type Distance float64
type Time float64
func speed(a Distance, b Time) Speed {
return Speed(float64(a) / float64(b))
}
func main() {
s := Distance(123.0)
t := Time(300)
fmt.Println(speed(s, t))
}
Can I make it more optimal by removing somehow casting to float64 in speed function?
No, you cannot avoid casting your distance and time back into floats because the division is not defined for those types. And as previously said, Go is strongly typed.
So, in your case you'd have to put casts everywhere (not a good idea). Type aliasing is good if you want to write custom methods for your types, but its purpose is not to solely hide the underlying type under a custom one.
However, not all type are working this way. If you make an alias of a map, then you can call the bracket operators without problem.
type Map map[string]string
func main() {
m := Map(make(map[string]string))
m["answer"] = "42"
fmt.Printf("m's type is %T and answer is %s\n", m, m["answer"])
//
// m's type is main.Map and answer is 42
}
Also, when initializing your custom aliases, casting is unnecessary:
type Speed float64
type Distance float64
func main() {
var s Distance = 123.0
var t Time = 300
// ...
}
This compiles and works perfectly. What happens behind the scene is that the literal 123.0 is considered as an untyped float and 300 is considered as an untyped int.
I know this sounds weird but basically those values are not typed so Go tries to fit them into the type at the left. This is why you can write var f float64 = 1 even though 1 is not a float. But you can't write var f float64 = int(1) because 1 becomes a typed int which cannot be translated in a float64.
This is why the following won't work:
func main() {
var distance float64 = 123.0
var time float64 = 300
var s Distance = distance
var t Time = time
// ...
}
You can't make implicit casts between custom types-- Go is strongly typed.
I know this is just a small example, but maybe you really don't need those custom types?
package main
import "fmt"
func speed(distance float64, time float64) float64 {
return distance / time
}
func main() {
s := 123.0
t := 300.0
fmt.Println(speed(s, t))
}

Idiomatic Type Conversion in Go

I was playing around with Go and was wondering what the best way is to perform idiomatic type conversions in Go. Basically my problem lays within automatic type conversions between uint8, uint64, and float64. From my experience with other languages a multiplication of a uint8 with a uint64 will yield a uint64 value, but not so in go.
Here is an example that I build and I ask if this is the idiomatic way of writing this code or if I'm missing an important language construct.
package main
import ("math";"fmt")
const(Width=64)
func main() {
var index uint32
var bits uint8
index = 100
bits = 3
var c uint64
// This is the line of interest vvvv
c = uint64(math.Ceil(float64(index * uint32(bits))/float64(Width)))
fmt.Println("Test: %v\n", c)
}
From my point of view the calculation of the ceiling value seems unnecessary complex because of all the explicit type conversions.
Thanks!
There are no implicit type conversions for non-constant values.
You can write
var x float64
x = 1
But you cannot write
var x float64
var y int
y = 1
x = y
See the spec for reference.
There's a good reason, to not allow automatic/implicit type conversions, as they can
become very messy and one has to learn many rules to circumvent the various caveats
that may occur. Take the Integer Conversion Rules in C for example.
For example,
package main
import "fmt"
func CeilUint(a, b uint64) uint64 {
return (a + (b - 1)) / b
}
func main() {
const Width = 64
var index uint32 = 100
var bits uint8 = 3
var c uint64 = CeilUint(uint64(index)*uint64(bits), Width)
fmt.Println("Test:", c)
}
Output:
Test: 5
To add to #nemo terrific answer. The convenience of automatic conversion between numeric types in C is outweighed by the confusion it causes. See https://Golang.org/doc/faq#conversions. Thats why you can't even convert from int to int32 implicitly. See https://stackoverflow.com/a/13852456/12817546.
package main
import (
. "fmt"
. "strconv"
)
func main() {
i := 71
c := []interface{}{byte(i), []byte(string(i)), float64(i), i, rune(i), Itoa(i), i != 0}
checkType(c)
}
func checkType(s []interface{}) {
for k, _ := range s {
Printf("%T %v\n", s[k], s[k])
}
}
byte(i) creates a uint8 with a value of 71, []byte(string(i)) a []uint8 with [71], float64(i) float64 71, i int 71, rune(i) int32 71, Itoa(i) string 71 and i != 0 a bool with a value of true.
Since Go won't convert numeric types automatically for you (See https://stackoverflow.com/a/13851553/12817546) you have to convert between types manually. See https://stackoverflow.com/a/41419962/12817546. Note, Itoa(i) sets an "Integer to an ASCII". See comment in https://stackoverflow.com/a/10105983/12817546.

Resources