Арифметические операции и приведение типов: особенности Go
Краткое описание
В этом уроке изучаем базовые арифметические операции в Go и ключевые отличия от других C-подобных языков. Разбираем целочисленное деление, остаток от деления, и самое важное — почему в Go нет автоматического приведения типов и как правильно работать с явным приведением (type casting).
Ключевые концепции
Пять основных операций
Go поддерживает стандартный набор арифметических операций:
var a int = 14
var b int = 5
sum := a + b // сложение: 19
difference := a - b // вычитание: 9
product := a * b // умножение: 70
quotient := a / b // деление: 2 (целочисленное!)
remainder := a % b // остаток: 4
Важно: все операции — бинарные, то есть применяются к двум операндам.
Целочисленное деление — главная особенность
Ключевое отличие Go: если оба операнда целые числа, результат деления тоже будет целым числом (неполное частное).
var a int = 12
var b int = 5
result := a / b // result = 2, а НЕ 2.4!
Как это работает
Вспоминаем деление столбиком из школы:
12 : 5 = 2 (неполное частное)
10
--
2 (остаток)
В целочисленном делении мы берём только неполное частное (цифра 2), а дробную часть отбрасываем.
Математическое объяснение:
- 12 ÷ 5 = 2 (неполное частное)
- 12 - (5 × 2) = 2 (остаток)
a := 13
b := 5
quotient := a / b // 2 (неполное частное)
remainder := a % b // 3 (остаток: 13 - 5×2 = 3)
Остаток от деления (модуль)
Оператор % возвращает остаток от целочисленного деления:
14 % 5 // 4 (14 = 5×2 + 4)
13 % 5 // 3 (13 = 5×2 + 3)
12 % 5 // 2 (12 = 5×2 + 2)
10 % 5 // 0 (10 = 5×2 + 0)
Формула: a % b = a - (a/b)*b
Важно: оператор % работает только с целыми числами. Для вещественных чисел используйте функцию math.Mod().
Отсутствие автоматического приведения типов
Главное отличие от C#, Java, JavaScript: в Go нет автоматического приведения типов в арифметических операциях.
В других языках (C#, Java)
int a = 10;
double b = 3.0;
var result = a / b; // 3.333... (автоматически int → double)
В Go — ошибка компиляции
var a int = 10
var b float64 = 3.0
// result := a / b // ОШИБКА! invalid operation: a / b (mismatched types int and float64)
Go требует явного приведения типов.
Явное приведение типов (Type Casting)
Чтобы выполнить арифметическую операцию с разными типами, нужно явно привести один из операндов к типу другого.
Приведение целого к вещественному
var a int = 14
var b int = 5
result := float64(a) / float64(b) // 2.8
fmt.Printf("%.1f\n", result)
Или проще:
result := float64(a) / float64(b)
Приведение вещественного к целому
var x float64 = 5.0
var y int = 14
result := y / int(x) // 2 (целочисленное деление)
Внимание: при приведении float → int дробная часть отбрасывается (округление в меньшую сторону):
int(5.0) // 5
int(5.3) // 5 (потеря данных!)
int(5.9) // 5 (не 6!)
Тип результата
Тип результата арифметической операции всегда совпадает с типом операндов:
var a int = 10
var b int = 3
result1 := a / b // result1 имеет тип int (= 3)
result2 := float64(a) / float64(b) // result2 имеет тип float64 (= 3.333...)
Важно: даже если присваиваете результат в переменную другого типа, нужно явное приведение:
var result float64
var a int = 10
var b int = 3
// result = a / b // ОШИБКА! cannot use a / b (value of type int) as float64
result = float64(a) / float64(b) // правильно
Практические примеры
Полный пример базовых операций
package main
import "fmt"
func main() {
fmt.Println("--- Основные операторы: +, -, *, /, % ---")
var a int = 14
var b int = 5
// Сложение
sum := a + b
fmt.Printf("%d + %d = %d\n", a, b, sum)
// 14 + 5 = 19
// Вычитание
difference := a - b
fmt.Printf("%d - %d = %d\n", a, b, difference)
// 14 - 5 = 9
// Умножение
product := a * b
fmt.Printf("%d * %d = %d\n", a, b, product)
// 14 * 5 = 70
// Деление (целочисленное!)
quotient := a / b
fmt.Printf("%d / %d = %d (целочисленное)\n", a, b, quotient)
// 14 / 5 = 2
// Остаток от деления
remainder := a % b
fmt.Printf("%d %% %d = %d (остаток)\n", a, b, remainder)
// 14 % 5 = 4
}
Приведение типов для обычного деления
var a int = 14
var b int = 5
// Вариант 1: приведение обоих к float64
result1 := float64(a) / float64(b)
fmt.Printf("%.2f\n", result1) // 2.80
// Вариант 2: приведение только одного (достаточно)
result2 := float64(a) / float64(b)
fmt.Printf("%.2f\n", result2) // 2.80
Смешивание типов (с явным приведением)
var intNum int = 14
var floatNum float64 = 5.0
// Деление int на float (приводим int к float64)
result1 := float64(intNum) / floatNum
fmt.Printf("%.1f\n", result1) // 2.8
// Деление int на int (приводим float64 к int)
result2 := intNum / int(floatNum) // целочисленное деление
remainder := intNum % int(floatNum)
fmt.Printf("Частное: %d, Остаток: %d\n", result2, remainder)
// Частное: 2, Остаток: 4
Приведение с потерей данных
var precise float64 = 5.3
// Приведение float → int отбрасывает дробную часть
asInt := int(precise)
fmt.Println(asInt) // 5 (НЕ 6! Округление не происходит)
// Пример вычисления
var a int = 14
result := a / int(5.9) // int(5.9) = 5, затем 14 / 5 = 2
fmt.Println(result) // 2
Приведение между целочисленными типами
var bigInt int64 = 1000
var smallInt int8 = int8(bigInt) // приведение int64 → int8
fmt.Println(smallInt) // работает, если значение помещается
// Осторожно с переполнением!
var huge int64 = 300
var tiny int8 = int8(huge) // переполнение! результат непредсказуем
fmt.Println(tiny) // -44 (из-за переполнения)
Важные моменты
1. Целочисленное деление по умолчанию
Если оба операнда типа int, деление всегда целочисленное. Дробная часть отбрасывается, округление не происходит.
2. Нет автоматического приведения типов
В отличие от C#, Java, JavaScript — в Go нельзя смешивать типы без явного приведения. Это предотвращает ошибки и делает код более предсказуемым.
3. Оператор % только для целых чисел
14 % 5 // OK: 4
// 14.5 % 5.2 // ОШИБКА! invalid operation
Для вещественных чисел используйте math.Mod(x, y):
import "math"
result := math.Mod(14.5, 5.2) // 4.1
4. Приведение float → int теряет дробную часть
int(3.14) // 3
int(3.99) // 3 (не 4!)
int(-2.9) // -2
Это отбрасывание, а не округление. Для округления используйте math.Round(), math.Floor(), math.Ceil().
5. Тип результата = тип операндов
var a int = 10
var b int = 3
result := a / b // тип result — int, значение 3
resultFloat := float64(a) / float64(b) // тип float64, значение 3.333...
6. Проверяйте переполнение при приведении
var big int64 = 1000000
var small int8 = int8(big) // переполнение! непредсказуемый результат
Компилятор не предупредит о потенциальном переполнении.
7. Оба операнда должны быть одного типа
var a int32 = 10
var b int64 = 5
// result := a / b // ОШИБКА! mismatched types
result := int64(a) / b // OK
Даже разные размеры int несовместимы без явного приведения.
8. Деление на ноль — паника
var a int = 10
var b int = 0
// result := a / b // ПАНИКА! runtime error: integer divide by zero
Всегда проверяйте делитель перед операцией:
if b != 0 {
result := a / b
}
Best Practices
1. Явное приведение для читаемости
Даже если приведение не обязательно, делайте его явным для ясности:
// Менее понятно
result := float64(a/b) // сначала целочисленное деление, потом приведение
// Более понятно
result := float64(a) / float64(b) // явное намерение: вещественное деление
2. Проверяйте деление на ноль
func safeDivide(a, b int) int {
if b == 0 {
return 0 // или возвращайте ошибку
}
return a / b
}
3. Используйте правильные функции для округления
import "math"
// Отбрасывание дробной части
truncated := int(3.9) // 3
// Математическое округление
rounded := int(math.Round(3.9)) // 4
// Округление вниз
floored := int(math.Floor(3.9)) // 3
// Округление вверх
ceiled := int(math.Ceil(3.1)) // 4
4. Будьте осторожны с приведением к меньшим типам
var value int64 = 300
// Опасно без проверки
small := int8(value) // переполнение возможно
// Безопаснее
if value >= math.MinInt8 && value <= math.MaxInt8 {
small := int8(value)
}
5. Используйте константы для избежания приведений
const divisor = 5.0 // константа типизируется автоматически
var a int = 14
result := float64(a) / divisor // divisor подстраивается под контекст
6. Документируйте потерю точности
// convertToInt приводит число к целому, отбрасывая дробную часть
func convertToInt(value float64) int {
return int(value) // дробная часть теряется
}
Что запомнить
- Go поддерживает пять базовых арифметических операций:
+,-,*,/,% - Деление целых чисел всегда целочисленное (неполное частное)
%возвращает остаток от деления (работает только с целыми числами)- В Go нет автоматического приведения типов (отличие от C#, Java, JS)
- Для смешивания типов нужно явное приведение:
float64(x),int(y) - Приведение
float → intотбрасывает дробную часть (не округляет) - Тип результата операции всегда совпадает с типом операндов
- Деление на ноль вызывает панику — всегда проверяйте делитель
- Для вещественного остатка используйте
math.Mod() - Для округления используйте
math.Round(),math.Floor(),math.Ceil() - Приведение к меньшим типам может вызвать переполнение
- Даже
int32иint64несовместимы без явного приведения - Явное приведение делает код безопаснее и понятнее