Деление на ноль и переполнение
До сих пор ошибка значила «программа не собралась». Сегодня — ошибка другого рода: программа собралась, запустилась и упала посреди работы. В Go это паника. Разберём, откуда она берётся при делении и как читать её сообщение.
Деление на ноль, которое видит компилятор
fmt.Println(120 / 0)stagiaire@lab:~/gocourse/day03/scratch$ go run .
# day03/scratch
./main.go:6:20: invalid operation: division by zeroНа ноль делить нельзя, и компилятор это видит: ноль записан в коде. То же, если слева переменная, а справа константа:
orders := 120
fmt.Println(orders / 0)stagiaire@lab:~/gocourse/day03/scratch$ go run .
# day03/scratch
./main.go:7:23: invalid operation: division by zeroТо же и с именованной константой из дня 02 (const couriers = 0): её значение компилятору известно.
Паника: деление на ноль во время работы
Ситуация. Заказы надо поровну раздать курьерам, а курьеров ноль.
package main
import "fmt"
func main() {
orders := 120
couriers := 0
fmt.Println("Заказов всего:", orders)
fmt.Println("На одного курьера:", orders/couriers)
fmt.Println("Готово")
}Что увидите.
stagiaire@lab:~/gocourse/day03/scratch$ go run .
Заказов всего: 120
panic: runtime error: integer divide by zero
goroutine 1 [running]:
main.main()
/home/stagiaire/gocourse/day03/scratch/main.go:9 +0x65
exit status 2Программа собралась: что лежит в переменной couriers, компилятор не отслеживает. Первая строка напечаталась, на девятой программа встала, «Готово» не появилось. Строка goroutine 1 [running]: говорит, в какой «нити» выполнения это случилось; горутины — в блоке 11.
Как это читать.
| Строка | Что значит |
|---|---|
panic: runtime error: integer divide by zero |
что случилось: ошибка времени выполнения (runtime), целое деление на ноль |
main.main() |
в какой функции: main из пакета main |
…/scratch/main.go:9 +0x65 |
где: файл и номер строки; +0x65 — место в машинных командах, его пропускаем |
exit status 2 |
это пишет уже go run: программа завершилась с кодом 2, то есть аварийно |
Порядок чтения паники: первая строка — что, строка с main.go:N — где. Дальше как с ошибкой компилятора: открыть строку и понять, откуда там ноль. Собранный файл, запущенный как ./scratch, напечатает то же, но без exit status 2 — это слова go run.
Остаток от деления на ноль — та же паника. А дробные числа не паникуют: у float64 есть «бесконечность», и weight / boxes при boxes := 0.0 печатает +Inf. Программа не упадёт, но +Inf пойдёт дальше в расчёты — ещё одна причина не считать деньги в float64. Проверять делитель — в день 06, когда появится if.
Попробуйте сейчас: где упадёт?
Цель: найти без запуска строку, на которой программа остановится с паникой, и отправить её номер.
Код для чтения · разбираем, набирать не нужноpackage main import "fmt" func main() { stock := 480 perPallet := 60 pallets := stock / perPallet trucks := pallets / 10 fmt.Println("Паллет:", pallets) fmt.Println("Машин:", trucks) fmt.Println("Паллет на машину:", pallets/trucks) }Строки считаются от начала файла:
package main— строка 1. Ответьте номером строки, который будет в сообщении паники послеmain.go:.▶ Выполните · выделенное замените своимcourse answer day03.q5 ЧИСЛОПотом проверьте себя запуском — программу нужно набрать в
scratch/main.go:✎ Наберите в файлscratch/main.gopackage main import "fmt" func main() { stock := 480 perPallet := 60 pallets := stock / perPallet trucks := pallets / 10 fmt.Println("Паллет:", pallets) fmt.Println("Машин:", trucks) fmt.Println("Паллет на машину:", pallets/trucks) }▶ Выполнитеcd ~/gocourse/day03/scratch go run .Найдите в выводе все части из таблицы выше.
Готово, когда: ответ отправлен — пункт проверки
q5.
Переполнение
У int есть предел: на 64-битном компьютере от −9223372036854775808 до 9223372036854775807. Число в коде больше предела не пропустит компилятор:
var n int = 9223372036854775808
fmt.Println(n)stagiaire@lab:~/gocourse/day03/scratch$ go run .
# day03/scratch
./main.go:6:14: cannot use 9223372036854775808 (untyped int constant) as int value in variable declaration (overflows)А если предел превышен во время работы, паники не будет: n := 9223372036854775807 и n = n + 1 печатают -9223372036854775808. Самое большое число плюс один молча стало самым маленьким — как счётчик пробега, перевалив за девятки. Для копеек предел — 92 квадриллиона рублей, складу хватит, но при умножении больших чисел о нём стоит помнить.
Что может пойти не так
| Что видите | Что это значит | Что делать |
|---|---|---|
invalid operation: division by zero |
делитель — ноль из кода или константы | исправить делитель |
panic: runtime error: integer divide by zero |
делитель-переменная во время работы оказался нулём | строка main.go:N — где; понять, откуда ноль |
+Inf или -Inf в выводе |
дробное деление на ноль | найти, где делитель стал нулём |
… as int value in variable declaration (overflows) |
число не помещается в int |
проверить число: лишние цифры? |
| огромное отрицательное число вместо положительного | переполнение во время работы | искать умножение больших чисел |
Словарик
К концу дня в ~/errors.md должно быть не меньше семи разборов — вместе с записями прошлых дней. Кандидаты — таблицы «Что может пойти не так» со всех страниц: truncated, mismatched types, cannot convert, сообщения go vet, %!d(MISSING), division by zero, паника. Формат прежний: текст ошибки, тире, что значило у вас. Разделитель — только тире (« — » или « - »): двоеточие есть в каждом сообщении компилятора.
Попробуйте сейчас: поймайте панику сами.
Цель: увидеть на одной программе панику, нормальный вывод и ошибку компиляции, а словарик дописать до семи разборов.
- В
scratch/main.goнаберите программу про курьеров из раздела «Паника»:✎ Наберите в файлscratch/main.gopackage main import "fmt" func main() { orders := 120 couriers := 0 fmt.Println("Заказов всего:", orders) fmt.Println("На одного курьера:", orders/couriers) fmt.Println("Готово") }
- Запустите:
▶ Выполнитеcd ~/gocourse/day03/scratch go run .
- Найдите в выводе строку с номером: совпадает ли он со строкой деления в VS Code?
- Поменяйте
couriers := 0наcouriers := 8— паника исчезнет, «Готово» напечатается.- Верните ноль и замените
orders/couriersнаorders/0— ошибка станет ошибкой компиляции, сообщений будет два:Пример · только посмотреть, набирать не нужноstagiaire@lab:~/gocourse/day03/scratch$ go run . # day03/scratch ./main.go:7:2: declared and not used: couriers ./main.go:9:58: invalid operation: division by zeroПервое — побочное: в строке 9 больше нет
couriers, и переменная из строки 7 нигде не читается (это вы видели в дне 02). Колонка 58 больше длины строки: русские буквы считаются по два байта, как в дне 01.
- Допишите словарик до семи разборов — каждый через тире.
Готово, когда: вы увидели панику, вывод с «Готово» и два сообщения компилятора, а в
~/errors.mdне меньше семи разборов — пунктerrors.
Материал дня закончен. Задания — course lab 03, дополнительное чтение — course extra 03