Числа: math и strconv
В дне 03 вы узнали, что 150 / 12 у целых чисел даёт 12, а дробь теряется. Но 150 кружек по 12 в коробке — это 13 коробок, иначе шесть кружек поедут без упаковки. Сегодня — функции, которые округляют как нужно и превращают число в строку.
Ситуация: коробки и цена
package main
import (
"fmt"
"math"
"strconv"
)
func main() {
qty := 150
perBox := 12
full := math.Floor(float64(qty) / float64(perBox))
boxes := math.Ceil(float64(qty) / float64(perBox))
fmt.Println("Полных коробок:", full)
fmt.Println("Коробок нужно:", boxes)
fmt.Println("Цена:", math.Round(1299.5), "руб.")
msg := "Заказ №" + strconv.Itoa(1042) + " собран"
fmt.Println(msg)
}Что увидите:
Полных коробок: 12
Коробок нужно: 13
Цена: 1300 руб.
Заказ №1042 собранmath: три способа округлить
| Функция | Что делает | Где пригодится |
|---|---|---|
math.Floor(x) |
вниз, до целого не больше x |
сколько коробок заполнится целиком |
math.Ceil(x) |
вверх, до целого не меньше x |
сколько коробок нужно, чтобы влезло всё |
math.Round(x) |
до ближайшего целого; половина — от нуля | цена в рублях на ценнике |
Результаты на Go 1.27:
x |
Floor |
Ceil |
Round |
|---|---|---|---|
1299.49 |
— | — | 1299 |
1299.5 |
— | — | 1300 |
1299.99 |
1299 |
— | — |
1299.01 |
— | 1300 |
— |
-2.5 |
-3 |
— | -3 |
Как читать программу выше. Все три функции принимают float64 и возвращают float64. Поэтому qty и perBox сначала превращаются в дробные: float64(qty) / float64(perBox) — это 12.5. Дальше Floor даёт 12, Ceil — 13.
Порядок здесь решает всё. Запись math.Ceil(float64(qty / perBox)) выглядит почти так же, но печатает 12: сначала выполняется целочисленное деление 150 / 12 = 12, дробь теряется, и округлять уже нечего. Превращайте в float64 до деления.
Println печатает float64 без лишней точки: 13, а не 13.0, — но тип от этого не меняется.
Деньги и float64. Запрет дня 03 в силе: цена хранится целым числом копеек. math.Round(1299.5) здесь не расчёт, а показ — округлить уже посчитанную сумму до рубля для ценника. Поэтому в задании label цена превращается в рубли записью math.Round(float64(priceKop) / 100): один float64 на последнем шаге, и обратно в расчёты он не возвращается.
strconv.Itoa — число в строку
Нужна строка Заказов: 17, а число лежит в orders типа int. Склеить напрямую нельзя:
./main.go:7:9: invalid operation: "Заказов: " + orders (mismatched types untyped string and int)strconv.Itoa превращает целое число в строку, и склейка работает:
orders := 17
msg := "Заказов: " + strconv.Itoa(orders)
fmt.Println(msg)Заказов: 17Название — от Integer to ASCII. Обратную функцию, strconv.Atoi, разберём в день 07. Itoa принимает только int: дробное число ей не подойдёт. Встроенные min и max и fmt.Sprintf — в course extra 04.
Что может пойти не так
| Что видите | Что это значит | Что делать |
|---|---|---|
./main.go:5:2: "math" imported and not used |
math подключён, а функции из него не вызываются |
убрать импорт или вызвать функцию |
./main.go:19:14: undefined: strings |
вызвана функция strings, а пакета нет в import |
добавить "strings" — Ctrl+S сам его не добавит |
cannot use boxes (variable of type float64) as int value in argument to strings.Repeat |
math.Ceil вернул float64, а Repeat ждёт int |
strings.Repeat("#", int(boxes)) |
invalid operation: "Заказов: " + orders (mismatched types untyped string and int) |
строку склеивают с числом | strconv.Itoa(orders) |
| коробок на одну меньше, чем нужно | float64 поставлен после целочисленного деления |
float64(qty) / float64(perBox) |
Попробуйте сейчас: три опыта с округлением.
Цель: увидеть
FloorиCeilна ровном делении,float64после деления иfloat64в аргументеRepeat.Наберите в черновике программу из «Ситуации»:
✎ Наберите в файлscratch/main.gopackage main import ( "fmt" "math" "strconv" ) func main() { qty := 150 perBox := 12 full := math.Floor(float64(qty) / float64(perBox)) boxes := math.Ceil(float64(qty) / float64(perBox)) fmt.Println("Полных коробок:", full) fmt.Println("Коробок нужно:", boxes) fmt.Println("Цена:", math.Round(1299.5), "руб.") msg := "Заказ №" + strconv.Itoa(1042) + " собран" fmt.Println(msg) }▶ Выполнитеcd ~/gocourse/day04/scratch go run .Потом:
- Поменяйте
qtyна144— ровно 12 коробок. Совпали лиFloorиCeil? Верните150.- Замените
math.Ceil(float64(qty) / float64(perBox))наmath.Ceil(float64(qty / perBox)). Сколько коробок? Верните как было.- Последней строкой
mainнапечатайте решётки по числу коробок:fmt.Println(strings.Repeat("#", boxes)). Пакетаstringsвimportнет,Ctrl+Sего не добавит — будетundefined: strings. Добавьте"strings"сами, запустите, прочитайте новую ошибку и почините черезint(...).Готово, когда: на вопросы шагов 1 и 2 есть ответы по запуску, программа из шага 3 печатает строку из решёток, а новые сообщения записаны в
~/errors.md(пунктerrors).
Попробуйте сейчас: что выведет, не запуская.
Цель: предсказать вывод по коду и отправить его.
Не запуская, предскажите вывод:
Код для чтения · разбираем, набирать не нужноpackage main import ( "fmt" "math" ) func main() { fmt.Println(math.Ceil(25.0/12), math.Round(25.0/12), 25/12) }
25.0/12— дробное деление,25/12— целочисленное. Отправьте строку вывода целиком:▶ Выполните · выделенное замените своимcourse answer day04.q3 ВАШ_ОТВЕТГотово, когда:
course answerответилПринято: day04.q3 = …(пункт проверкиq3).
Попробуйте сейчас: начать этикетку label.
Цель:
label/main.goпечатает строки с артикулом, ценой и числом коробок для вашего товара.▶ Выполнитеcd ~/gocourse/day04/label cat TASK.txtУ каждого свой товар. Создайте
main.go, объявите переменные из условия — каждую отдельной строкой, с тем же именем — и напечатайте строки с артикулом, ценой и числом коробок. Название и строку из=отложите до следующей страницы: там ловушка, из-за которой они выйдут неправильной ширины.Готово, когда:
go run .печатает строки с артикулом, ценой и числом коробок такими же, как вTASK.txt(пунктыlabelиlabel_funcsзасчитаются после следующей страницы, когда этикетка будет целиком).
Дальше: кириллица и len — course next