После изучения урока вы сможете:
- Как сэкономить место вместо нулей используя экспонент;
- Использование пакета
big
для крупных чисел; - Использование крупных констант и значений литералов.
Программирование построено на компромиссах. Числа с плавающей запятой float хранят значения любого размера, однако их точность временами оставляет желать лучшего. Целые числа integer точны, но их диапазон весьма ограничен. Что же делать, когда нужно использовать крупное, точное число? В данном уроке будут рассмотрены две альтернативы нативным типам float64
и int
.
Рекомендуем вам супер TELEGRAM канал по Golang где собраны все материалы для качественного изучения языка. Удивите всех своими знаниями на собеседовании! 😎
Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.
Содержание статьи
- Работа с крупными числовыми значениями в Go
- Пакет big для крупных чисел в Golang
- Константы нестандартного размера в Golang
Центральный процессор CPU оптимизирован для математических операций с целыми и вещественными числами, однако другие цифровые представления также возможны. Go может работать и с крупными значениями. В каких случаях целое число слишком мало, вещественное недостаточно точное, и какой-то другой цифровой тип будет более предпочтителен?
Работа с крупными числовыми значениями в Go
Это может быть очевидным не сразу, но 64-битные целые числа невообразимо огромны — намного больше, чем 32-битные.
Для сравнения, ближайшая к нам звезда Альфа Центавра находится на расстоянии 41.3 триллионов километров. Триллион записывается как единица с 12 нулями, или 1012. Неудобно каждый раз набирать такое количество нулей, поэтому в Go числа подобного рода можно записывать с экспонентой, как показано в примере:
1 |
var distance int64 = 41.3e12 |
int32
или uint32
не может работать с таким крупным значением, однако int64
справится без труда. Теперь мы можем рассчитать нужное значение. К примеру, сколько дней потребуется для полета до Альфа Центавры. Решение представлено ниже:
1 2 3 4 5 6 7 8 |
const lightSpeed = 299792 // км/ч const secondsPerDay = 86400 var distance int64 = 41.3e12 fmt.Println("Расстояние до Альфа Центавры составляет", distance, "км.") // Выводит: Расстояние до Альфа Центавры составляет 41300000000000 км. days := distance / lightSpeed / secondsPerDay fmt.Println("Это", days, "дня полета на скорости света.") // Выводит: Это 1594 дня полета на скорости света. |
Какими бы крупными не были 64-битные целые числа, всегда есть кое-что намного больше — космос. Галактика Андромеды находится на расстоянии 24 квинтиллионов (1018) километров от нас. Даже самые крупные неподписанные целые числа (uint64
) могут содержать значения до 18 квинтиллионов. При попытке объявить переменную, превышающую 18 квинтиллионов выйдет ошибка:
1 |
var distance uint64 = 24e18 // Ошибка: 24000000000000000000 overflows uint64 |
Паниковать не стоит — у вас несколько вариантов. Можно задействовать математику чисел с плавающей запятой. Это неплохая идея, тем более, что нам уже известно, как такие числа работают. Есть и другой способ. В следующей части рассмотрим один из пакетов Go — big
.
На заметку: Если тип переменной не уточняется, Go отнесет числа с экспонентой к типу
float64
.
Задание для проверки:
Расстояние от Земли до Марса варьируется от 56 000 000 км до 401 000 000 км. Представьте эти два значения в виде целых числе с помощью синтаксиса экспоненты (е).
Пакет big для крупных чисел в Golang
Пакет big
предоставляет три типа данных:
big.Int
для крупных целых чисел, когда 18 квинтиллионов недостаточно;big.Float
для вещественных чисел с плавающей запятой производной точности;big.Rat
для дробей вроде 1/3.
На заметку: Код Go также может назначать новые типы данных. Мы поговорим об этом в одном из следующих уроков.
Тип big.Int
может свободно хранить и оперировать таким крупным значением, как расстояние до галактики Андромеды, то есть около 24 квинтиллионов км.
Задействование big.Int
требует, чтобы оно использовалось для всех чисел в уравнении, даже если определенные константы использовались ранее. Функция NewInt
принимает значение int64
и возвращает big.Int
:
1 2 |
lightSpeed := big.NewInt(299792) secondsPerDay := big.NewInt(86400) |
NewInt
не поможет числу вроде 24 квинтиллионов. Оно не подойдет для int64
, поэтому вместо этого можно создать big.Int
из строки string
:
1 2 |
distance := new(big.Int) distance.SetString("24000000000000000000", 10) |
После создания нового big.Int
дадим ему значение в 24 квинтиллиона, вызвав метод SetString
. В основе 24 квинтиллионов 10 (десятичное значение), поэтому вторым аргументом будет 10.
Со всеми значениями на местах метод Div
осуществляет необходимое деление, так что результат может отображаться как в листинге ниже:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package main import ( "fmt" "math/big" ) func main() { lightSpeed := big.NewInt(299792) secondsPerDay := big.NewInt(86400) distance := new(big.Int) distance.SetString("24000000000000000000", 10) fmt.Println("Расстояние до галактики Андромеды составляет", distance, "км.") // Выводит: Расстояние до галактики Андромеды составляет 24000000000000000000 км. seconds := new(big.Int) seconds.Div(distance, lightSpeed) days := new(big.Int) days.Div(seconds, secondsPerDay) fmt.Println("Это", days, "дня полета на скорости света.") // Выводит: Это 926568346 дня полета на скорости света. } |
Как видно, данные большие типы более громоздки для работы, нежели нативные типы int и float64. Они также медленнее. Это компромиссы, на которые нужно идти ради точности и представления чисел любого размера.
Вопрос для проверки:
Назовите два способа добиться big.Int
для числа 86400.
Константы нестандартного размера в Golang
Константы, как и переменные, могут объявляться с указанием типа. И, также как и переменные, константы типа uint64
не могут содержать числа вроде 24 квинтиллионов:
1 |
const distance uint64 = 24000000000000000000 // Выводит: Константа 24000000000000000000 переполняет uint64 |
При объявлении константы без указания типа происходит нечто интересное. Переменным Go автоматически назначает тип, в случае с 24 квинтиллионами это будет переполненный тип int
. С константами все несколько иначе. Вместо автоматического назначения типа, константы могут стать нетипизированными. Следующая строка не выводит никаких ошибок переполнения:
1 |
const distance = 24000000000000000000 |
Константы объявляются с помощью ключевого слова const
, однако каждое значение литерала в программе также является константой. Это значит, что числа нестандартного размера могут использоваться напрямую, как показано далее:
1 |
fmt.Println("Расстояние до галактики Андромеды составляет", 24000000000000000000/299792/86400, "световых дней.") // Выводит: Расстояние до галактики Андромеды составляет 926568346 световых дней. |
Вычисления над константами и литералами осуществляются во время компиляции, а не во время работы программы. Компилятор Go написан на Go. Нетипированные цифровые константы поддерживаются пакетом big
, позволяя использовать все обычные операции с числами, значение которых 18 квинтиллионов, как показано в коде ниже:
1 2 3 4 5 6 7 |
const distance = 24000000000000000000 const lightSpeed = 299792 const secondsPerDay = 86400 const days = distance / lightSpeed / secondsPerDay fmt.Println("Расстояние до галактики Андромеды составляет", days, "световых дней.") // Выводит: Расстояние до галактики Андромеды составляет 926568346 световых дней. |
Значения констант могут присваиваться переменным, пока размер позволяет. int
не может вместить 24 квинтиллиона, но 926 568 346 поместится без проблем:
1 2 |
km := distance // Константа 24000000000000000000 переполняет int days := distance / lightSpeed / secondsPerDay // 926568346 помещается в int |
Существует предостережение для констант необычного размера. Хотя компилятор Go использует пакет big
для нетипизированных числовых констант, константы и значения big.Int
не являются взаимозаменяемыми. В листинге 2 показано значение big.Int
, что содержит 24 квинтиллиона, но вы не можете отобразить distance
из-за ошибки переполнения:
1 |
fmt.Println("Расстояние до галактики Андромеды составляет", distance, "км.") // Константа 24000000000000000000 переполняет int |
Очень крупные константы определенно полезны, но они не заменят пакет big.
Вопрос для проверки:
Когда проводятся вычисления над константами и литералами?
Заключение
- Когда диапазона значений нативных типов не хватает, поможет пакет
big
; - Крупные значения возможны с нетипизированными константами, и все численные литералы также являются нетипизированными константами;
- При передаче к функциям нетипизированные константы должны быть конвертированы в типизированные переменные.
Итоговое задание для проверки:
Карликовая галактика в Большом Псе является ближайшей известной к Земле галактикой, что находится на расстоянии 236 000 000 000 000 000 км от нашего Солнца. Используйте константы для конвертации данного расстояния в световые годы.
Администрирую данный сайт с целью распространения как можно большего объема обучающего материала для языка программирования Go. В IT с 2008 года, с тех пор изучаю и применяю интересующие меня технологии. Проявляю огромный интерес к машинному обучению и анализу данных.
E-mail: vasile.buldumac@ati.utm.md
Образование
Технический Университет Молдовы (utm.md), Факультет Вычислительной Техники, Информатики и Микроэлектроники
- 2014 — 2018 Universitatea Tehnică a Moldovei, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Universitatea Tehnică a Moldovei, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»