Числа

Набор числовых типов и операций с ними в Ficus схож с языками C/C++ и с большинством других языков программирования.

Одно существенное отличие от C/C++ и Python касается типа int. В C/C++ тип int чаще всего представляет собой 32-битное целое число, даже на 64-битных машинах. В Python начиная с версии 3.x тип int — это целое число произвольной точности. В Ficus тип int на 32-битных машинах является 32-битным числом, а на 64-битных — 64-битным. Важное замечание: в Ficus нет типа uint или unsigned.

Числовые типы не преобразуются друг в друга неявно. Например, если функция принимает аргумент типа int, а у вас имеется значение типа uint8, его нужно явно привести к типу int. Существуют три способа приведения числа из одного типа в другой:

  1. через оператор приведения типа (expr :> целевой_тип):
val a = 34587345
val b = 987654321
val product = (a :> uint64) * b

(Оператор :> универсален и может также использоваться для преобразования чисел в строки, запроса интерфейсов класса и т.п.; подробности см. в разделе Объектно-ориентированное программирование.)

  1. через специальную функцию преобразования: целевое_имя_типа() или sat_целевое_имя_типа(), например, int8() преобразует число в тип int8. Мы уже встречали функцию string(), которая преобразует различные значения в строку. Функции вида sat_[u]intN() выполняют преобразование с насыщением, то есть обрезают аргумент вместо простого отбрасывания младших бит:
// псевдо-реализация sat_uint8()
fun sat_uint8(x: int) =
    if x < 0 {0u8} else if x > 255 {255u8} else {uint8(x)}
  1. конверсия вещественного числа в целое — особый случай, для этого предусмотрены несколько функций:
  • round() — округление к ближайшему числу
  • floor() — округление к минус бесконечности
  • ceil() — округление к плюс бесконечности
  • trunc() — округление к нулю, аналогично C

Стандартный набор арифметических, поразрядных и сравнительных операторов в Ficus повторяет большинство других языков. Полный перечень доступных операций смотрите в разделе Выражения.

Аргументы бинарных операций могут иметь разные типы, в таких случаях применяются правила повышения типов, то есть оба аргумента приводятся к общему, достаточно широкому типу:

  1. до приведения, если T1 или T2 является типом int8, uint8, int16 или uint16, он приводится к типу int.
  2. если оба типа совпадают (возможно, после предыдущего шага), результатом повышения будет именно этот общий тип.
  3. иначе, если один из типов — целое число, а другой — вещественное, результатом станет вещественный тип.
  4. если один из типов — int или int32, а другой — int64, результатом будет int64.
  5. если один из типов — uint32, а другой — int64 или uint64, результатом будет int64 или uint64.
  6. если один из типов — float, а другой — double, результатом будет double.
  7. в противном случае проверщик типов выдаст ошибку, и нужно явно приводить аргументы к паре согласуемых типов.

Правила повышения применяются рекурсивно для массивов и кортежей при операциях .+, .-, .*, ./, при этом обработка осуществляется поэлементно.

Кроме того, есть полезные функции, работающие с числами:

  • min(a, b), max(a, b) — находят минимум/максимум двух чисел
  • abs(x) — вычисляет абсолютное значение аргумента
  • sign(x) — вычисляет знак аргумента (-1, 0 или 1)
  • sqr(x) — возводит аргумент в квадрат, то есть x*x
  • sqrt(x) — извлекает квадратный корень
  • sin(x), cos(x), exp(x), log(x), atan2(y, x) и прочие стандартные математические функции из модуля Math, подключаемого автоматически.