Const is a keyword we use a lot in Go, and it’s almost impossible to play with flowers. But there are special cases where const can have unexpected consequences

Scenario simulation

A company in a marketing activity, according to the user VIP level will send users some coupons, the largest face value of 520. A user found that he bought 500 yuan of goods, using 520 coupons to pay, theoretically can buy 0 yuan of goods, but finally need to pay an astronomical number.

Forgive me if I exaggerate this scenario. ^^ Here’s a code simulation of the scenario:

func main(a) {
	var totalPrice uint32 = 500
	const couponPrice = 550

	fmt.Println("Amount to be paid by the user:", totalPrice-couponPrice)
}
Copy the code

Before you run the program, what do you think it should return?

A. Program cannot be compiled B. -50 C. 50 D. 4294967246Copy the code

Would you be surprised if it came out D?

Some questions:

  1. Why is 500-550 not -50?
  2. Have you ever noticed const types?
  3. If you look at const types, why does the program compile?

Why is 500-550 not -50?

Rewrite and write a new piece of code, let’s make the value a little smaller, easy to explain later

func main(a) {
	var totalPrice uint8 = 1
	var couponPrice uint8 = 2
	fmt.Println("Amount to be paid by the user:", totalPrice-couponPrice)
}
Copy the code

Result: User needs to pay amount: 255

Have you ever heard of source code, inverse code and complement? If you don’t know, read on to find out the astronomical truth.

The original code

How do we identify 1 and -1 using binary?

1:0000 0001-1:1000 0001Copy the code

We can quickly find through comparison that the first is a sign bit, which is actually easy to understand and calculate a way of representation, this representation is called: source code. The computer itself only recognizes 0(low potential) and 1(high potential), which is actually the conduction and blocking of the diode in the LOGICAL operation unit of the CPU. So if you have a 1 for a sign bit that a computer can recognize, and you don’t have to count that bit, it’s basically unrealistic.

How to do this? Did someone once say that any problem in computer science can be solved by adding an indirect middle layer? Also the source code can be converted into binary code that the computer can recognize.

Radix-minus-one complement

Now, let’s look at another representation, which is 1 minus 1 equals 0. Suppose we count the sign bit, and leave the binary of the positive number unchanged. The binary sign bits of negative numbers remain the same, and the other bits are bitwise reversed.

1-1 => 0000 0001 + 1111 1110 = 1111 1111Copy the code

This is actually the inverse code. We convert 1111 1111 to a source code that we can recognize which is 1000 0000, which is actually minus 0. Then there is another problem, 0000 0000 also represents 0. So there is the source code of 1000 0000 and 0000 0000 which both represent 0. That’s not going to work.

complement

Hunch. Also involve the sign bit in calculation, we make a positive binary remains the same, negative number of binary symbol remains unchanged, the rest you invert, + 1, finally actually negative complement is the first to find the number of radix-minus-one complement, and then add 1 radix-minus-one complement.

So 1-1 can be identified as:

1-1 = > = > 0000 0001 + 1111 1110 0000 0001 + 1111 = 1111 > 0000, 0000 (complement) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- radix-minus-one complement radix-minus-one complement Complement complementCopy the code

The complement code 0000 0000 is also the original code, which is 0. This is no problem.

1 minus 2 problem

Let’s use complement to explain the results of 1-2:

1-2 => [0000 0001] + [1111 1101] => [0000 0001] + [1111 1110] + 1111 1111Copy the code

Convert the complement 1111 1111 into the original code 1000 0001 => -1

Are you wondering here again? The result is clearly: 4294967295.

For those of you who are writing Java, the vast majority of cases where int represents an integer will suffice. Php is even more presumptuous, not only does it not care about the size of the number, it is possible to pass you a “2” to use as an integer 2. But have you noticed that golang has signed and unsigned numbers like Int8 and uint8?

So, as you can see, subtraction of signed numbers is pretty much within our range. So why are unsigned numbers so magical?

Unsigned number

func main(a) {
	var totalPrice uint8 = 1
	var couponPrice uint8 = 2
	fmt.Println("Amount to be paid by the user:", totalPrice-couponPrice)
}
Copy the code

Again, we see that totalPrice and couponPrice are both integers of type uint32, and they are both unsigned integers. So when you see a program defining a variable using uintx, that variable is unsigned.

Why doesn’t Go use an int like Java, and what’s the point of having unsigned types?

  1. Signed numbers can represent negative numbers. For example, the range of int8 is [-128, 127]. In some scenarios where we only want positive numbers, we can use unsigned numbers. Similarly uint8 can represent [0, 255].
  2. In fact, I think it’s more likely that Go was written by the people who wrote C, and they continued to use this traditional numerical representation in C.

In terms of subtraction of unsigned numbers, we’re going to treat 1 minus 2 as the same thing as 1 plus minus 2, so we’re going to have to convert minus 2 to complement as well.

1-2 => [0000 0001] + [1111 1101] => [0000 0001] + [1111 1110] + 1111 1111Copy the code

Since the result of adding or subtracting an unsigned number is still an unsigned number, 1111 1111 is an unsigned number, so the highest bit is not a sign bit. The complement of positive numbers is the same as the original code, so the result of 1-2 is 255.

We’re using uint8, what if we use uint16, uint32, uint64? The same 1-2 for uint64 results in 18446744073709551615. Is it exaggerated??

We talked about subtraction, but what about addition? I’m not going to expand on this, but you’re smart enough to try the following code and think why?

Func main() {var uint8 = 255 var couponPrice = 1 uint.println (" how much does the user need to pay: ", totalPrice+couponPrice)}Copy the code

Finally, the calculation process of the unsigned type of Go is finished. From this, we can see that the following points should be paid attention to when adding or subtracting the budget of Go:

  1. When selecting variables of different types, pay special attention to the value range of the type to prevent the value from crossing the boundary
  2. Save computer resources. If int8 is used for a variable declaration, the uint32 is used for four bytes
  3. When subtracting variables of type Uint family, be sure to determine the size of the variable, otherwise the scene I mentioned at the beginning is what will happen on your line.

Have you ever noticed const types?

func main(a) {
	var totalPrice uint8 = 1
	var couponPrice int8 = 2
	fmt.Println("Amount to be paid by the user:", totalPrice+couponPrice)
}
Copy the code

What do you think the result of this code is?

A. Program cannot be compiled B.-1 C.255 D. 4294967246Copy the code

It turns out to be “A.” You’re surprised again. It should come as no surprise that Go does not support implicit conversions. Variables of different types cannot be converted directly to each other. They must be converted strongly. The above code is not compilable.

In fact, the type of direct strong turn is sometimes there will be problems. I’m not going to go into detail here, because it’s just going to add up. Please execute the results of the following code and analyze them, again for the reasons mentioned above, and be sure to pay attention to the range of types.

func main(a) {
	var a int64 = 922372037
	fmt.Println("a:", a)
	
	var b int8 = int8(a)
	fmt.Println("b:", b)
}
Copy the code

Results:

a: 922372037
b: -59
Copy the code

Going back to the const type, I guess most people don’t really care about const types

func main() {
	const a = 10
	fmt.Println("type:", reflect.TypeOf(a))
}
Copy the code

Result: type: int

Going back to the code at the beginning, why does it compile properly and output an incorrect result?

func main(a) {
	var totalPrice uint32 = 500
	const couponPrice = 550

	fmt.Println("Amount to be paid by the user:", totalPrice-couponPrice)
}
Copy the code

Const is still the same as the default type for ordinary variables. The default type for integer constants is int, the default type for floating-point constants is float64, and the default type for string constants is String

Here comes the special rule of Go:

In Go, there are no integer variables with default unsigned types. However, for flexibility, untyped const types can be used to assign values to unsigned types. This means that untyped const variables break Go’s rule that conversions between different types cannot be implicitly typed. An untyped const can then add, subtract, multiply or divide variables of the same type. This creates an astronomical number that is a uint32 minus an unsigned const int. The important thing to note here is that constant can be typed or untyped, and only untyped can be converted implicitly.

Some summary

  1. Go provides a variety of types of variables. If used correctly, the program can save more resources, but you should pay special attention to choose the appropriate types to avoid some puzzling problems. It is recommended that you use int as much as possible. On 32-bit systems int is int32, which is the same as Java int [-2147483648, 2147483647]. We really don’t have a shortage of resources on our current servers. Unless you really know how big this variable is.
  2. When doing an unsigned addition, be careful to first determine the size of the variable to be subtracted from.

Is const causing the result of the program to be confused? In fact, it is not all due to const. In the final analysis, it is due to the subtraction of unsigned numbers. Let’s be aware of this when we write Go. Of course, we can also pay attention to the const type, perhaps is your next interview questions.

After reading this article, do you have a new understanding of the type of Go? Welcome to follow my public account: HHFCodeRv Communication