· 8 мин 👁 1.5k Начинающий

Язык Gleam — числа

Int, Float и форматы записи чисел в Gleam: операторы, поведение при делении на ноль, отличия между BEAM и JavaScript, и сравнение с Go и Java.

gleamintfloatчислаарифметика
Содержание

Разбираем числа в 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 — научная нотация