После изучения урока вы сможете:
- Как сэкономить место вместо нулей используя экспонент;
- Использование пакета
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 км. Представьте эти два значения в виде целых числе с помощью синтаксиса экспоненты (е).
Решение
|
1 2 |
var distance int = 56e6 distance = 401e6 |
Пакет 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.
Ответ
Первый способ: Построим big.Int с функцией NewInt:
|
1 |
secondsPerDay := big.NewInt(86400) |
Второй способ: Используем метод SetString:
|
1 2 |
secondsPerDay := new(big.Int) secondsPerDay.SetString("86400", 10) |
Константы нестандартного размера в 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.
Вопрос для проверки:
Когда проводятся вычисления над константами и литералами?
Ответ на вопрос
Компилятор Go упрощает выражения, содержащие константы и литералы, во время компиляции.
Заключение
- Когда диапазона значений нативных типов не хватает, поможет пакет
big; - Крупные значения возможны с нетипизированными константами, и все численные литералы также являются нетипизированными константами;
- При передаче к функциям нетипизированные константы должны быть конвертированы в типизированные переменные.
Итоговое задание для проверки:
Карликовая галактика в Большом Псе является ближайшей известной к Земле галактикой, что находится на расстоянии 236 000 000 000 000 000 км от нашего Солнца. Используйте константы для конвертации данного расстояния в световые годы.
Решение
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package main import ( "fmt" ) func main() { const distance = 236000000000000000 const lightSpeed = 299792 const secondsPerDay = 86400 const daysPerYear = 365 const years = distance / lightSpeed / secondsPerDay / daysPerYear fmt.Println("Расстояние в световых годах до Карликовой галактики в Большом Псе составляет:", years) // Выводит: Расстояние в световых годах до Карликовой галактики в Большом Псе составляет: 24962 } |

Администрирую данный сайт с целью распространения как можно большего объема обучающего материала для языка программирования Go. В IT с 2008 года, с тех пор изучаю и применяю интересующие меня технологии. Проявляю огромный интерес к машинному обучению и анализу данных.
E-mail: vasile.buldumac@ati.utm.md
Образование
Технический Университет Молдовы (utm.md), Факультет Вычислительной Техники, Информатики и Микроэлектроники
- 2014 — 2018 Universitatea Tehnică a Moldovei, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Universitatea Tehnică a Moldovei, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»