Язык Gleam — числа
Int, Float и форматы записи чисел в Gleam: операторы, поведение при делении на ноль, отличия между BEAM и JavaScript, и сравнение с Go и Java.
Содержание
Разбираем числа в Gleam на примерах — и сравниваем с тем, как те же вещи устроены в Go и Java.
Int
Int в Gleam — целые числа. Арифметика, сравнения, равенство:
import gleam/int
pub fn main() {
// Арифметика
echo 1 + 1 // 2
echo 5 - 1 // 4
echo 5 / 2 // 2
echo 3 * 3 // 9
echo 5 % 2 // 1
// Сравнения
echo 3 > 1 + 1 // True
echo 2 < 1 - 1 // False
echo 8 >= 1 + 3 // True
echo 8 <= 5 - 3 // False
// Равенство работает для любого типа
echo 2 == 1 + 1 // True
echo 2 == 1 - 1 // False
// Функции из стандартной библиотеки
echo int.max(42, 77) // 77
echo int.clamp(5, 10, 20) // 10
}
int.clamp(value, min, max) ограничивает значение диапазоном: если value меньше min — возвращает min, если больше max — возвращает max. Удобно при работе с пользовательским вводом или координатами.
Деление целых чисел в Gleam — целочисленное: 5 / 2 даёт 2, остаток отдельно через %. Это то же поведение, что в Go (5 / 2 == 2) и Java (5 / 2 == 2). Без сюрпризов.
Важная деталь про размер Int: на виртуальной машине BEAM целые числа не имеют ограничения по размеру — можно работать с числами произвольной длины без переполнения. На JavaScript-рантайме Int представлен 64-битным числом с плавающей точкой, что накладывает ограничение: целые числа точно представляются только до 2⁵³. В Go int зависит от платформы — 32 или 64 бита, переполнение происходит молча. В Java int — всегда 32 бита, long — 64; для произвольной точности нужен BigInteger.
Float
Float — числа с дробной частью. Здесь Gleam отличается от большинства языков заметно: операторы для Float другие, с точкой на конце.
import gleam/float
pub fn main() {
// Арифметика — операторы с точкой
echo 1.0 +. 1.5 // 2.5
echo 5.0 -. 1.5 // 3.5
echo 5.0 /. 2.5 // 2.0
echo 3.0 *. 3.5 // 10.5
// Сравнения — тоже с точкой
let one = 1.0
echo 2.2 >. one // True
echo 2.2 <. one // False
echo 2.2 >=. one // True
echo 2.2 <=. one // False
// Равенство — обычное ==
echo 3.0 == 1.5 *. 2.0 // True
echo 2.1 == 1.2 +. 1.0 // False (особенности float)
// Деление на ноль — не ошибка, а ноль
echo 3.14 /. 0.0 // 0.0
// Функции из стандартной библиотеки
echo float.max(2.0, 9.5) // 9.5
echo float.ceiling(5.4) // 6.0
}
Почему операторы с точкой? В Gleam операторы не перегружены. + работает только с Int, +. — только с Float. Попытка сложить Int и Float через любой из этих операторов — ошибка компилятора. Нет неявных преобразований, нет неожиданного поведения.
В Go операторы перегружены неявно: + работает и с int, и с float64, но смешивать типы нельзя — тоже ошибка компилятора. В Java + работает с любыми числовыми типами, а int неявно повышается до float при необходимости. Это удобно, но скрывает потенциальные потери точности.
Поведение при переполнении и делении на ноль различается между рантаймами. На JavaScript Float может стать Infinity или -Infinity, а деление двух бесконечностей даёт NaN. На BEAM переполнение вызывает ошибку — NaN и Infinity там не существуют как значения. Деление на ноль в обоих случаях возвращает 0.0 — это явное решение в спецификации языка, а не случайность.
В Go деление float64 на ноль даёт +Inf или -Inf (стандарт IEEE 754), 0.0 / 0.0 даёт NaN. В Java — то же самое. Gleam на BEAM ведёт себя иначе: 3.14 /. 0.0 — это просто 0.0.
Строка echo 2.1 == 1.2 +. 1.0 возвращает False — классическая проблема чисел с плавающей точкой: 1.2 + 1.0 в двоичном представлении не равно ровно 2.1. Это не баг Gleam, это фундаментальное свойство IEEE 754, одинаковое во всех языках. Для сравнения float лучше использовать float.loosely_equals с допустимой погрешностью.
Форматы записи чисел
pub fn main() {
// Разделитель тысяч для читаемости
echo 1_000_000 // 1000000
echo 10_000.01 // 10000.01
// Int в двоичной, восьмеричной и шестнадцатеричной записи
echo 0b00001111 // 15
echo 0o17 // 15
echo 0xF // 15
// Float в научной нотации
echo 7.0e7 // 70000000.0
echo 3.0e-4 // 0.0003
}
Подчёркивание _ в числах — чисто визуальный разделитель, компилятор его игнорирует. 1_000_000 и 1000000 — одно и то же число. Это удобно для больших констант: 86_400 читается как «секунд в сутках» быстрее, чем 86400.
Префиксы 0b, 0o, 0x работают так же, как в Go и большинстве современных языков. Все три примера выше дают 15 — просто записанное по-разному. Шестнадцатеричная запись часто встречается при работе с цветами, битовыми масками и сетевыми протоколами.
Научная нотация для Float: 7.0e7 — это 7 × 10⁷, то есть 70_000_000.0. 3.0e-4 — это 3 × 10⁻⁴, то есть 0.0003. Удобно для физических констант или очень маленьких значений вероятностей.
В Go те же возможности: 1_000_000, 0xFF, 0b1010, 1.5e10. В Java подчёркивания в числах появились в Java 7, остальные форматы были с начала. Gleam здесь не изобретает ничего нового — просто следует современным соглашениям.
Равенство
pub fn main() {
echo 100 == 50 + 50 // True
echo 1.5 != 0.1 *. 10.0 // True
}
== и != работают с любым типом — числами, строками, списками,
пользовательскими типами. Единственное ограничение: обе стороны
оператора должны быть одного типа. Сравнить Int и Float напрямую
не получится — компилятор откажет.
Равенство в Gleam структурное: два значения равны, если у них
одинаковая структура, а не одинаковый адрес в памяти. Два списка
[1, 2, 3] и [1, 2, 3], созданные независимо, будут равны.
Это поведение интуитивно и совпадает с тем, чего ожидает
большинство разработчиков — в отличие от Java, где == для
объектов сравнивает ссылки, а не содержимое.
Итоги
Int— целые числа; на BEAM неограниченного размера, на JS — до 2⁵³Float— числа с точкой; операторы+.,-.,*.,/.— отдельные от целых- Операторы не перегружены: складывать
IntиFloatнапрямую нельзя - Деление
Floatна ноль возвращает0.0на обоих рантаймах - На BEAM нет
NaNиInfinity; на JS — есть, при переполнении _в числах — разделитель для читаемости,0b/0o/0x— альтернативные основания,e— научная нотация