ExpandCollapseNext Index

+ 1.1 Integers

Integers are not builtin types, but are in fact defined in the standard library by binding to their C equivalents.

+ 1.1.1 Integer types and literal suffix

In the following table, the Felix names of the basic integer types are given, along with the corresponding C names.

The table also lists the lower case versions of the type suffices allowed for literals of that type. Either or both the unsigned designator 'u' or the other part may be upper case instead (however lL is not permitted for long long, both letters must be either lower or upper case).

  Felix name  C name              Suffix
  -------------------------------------------------
  tiny        signed char         t
  utiny       unsigned char       ut, tu
  short       signed short int    s
  ushort      unsigned short int  us, su
  int         signed int          i (none) 
  uint        unsigned int        u
  long        signed long int     l
  ulong       unsigned long int   lu, ul
  vlong       signed long long    ll, v
  uvlong      unsigned long long  ull, llu, uv, vu

The following aliases for exact integers are also provided:

  Felix name  C name     Suffix
  -----------------------------
  int8        int8_t     i8
  uint8       uint8_t    u8
  int16       int16_t    i16
  uint16      uint16_t   u16
  int32       int32_t    i32
  uint32      uint32_t   u32
  int64       int64_t    i64/Users/johnskaller/felix/tut/func_01.expect
  uint64      uint64_t   u64

The following special aliases are also provided to use in low level code related to address calculations:

  Felix name    C name      Suffix
  --------------------------------
  size          size_t          uz
  ssize         ssize_t          z
  ptrdiff       ptrdiff_t        d
  uptrdiff      ptrdiff_t       ud
  intptr        intptr_t         p 
  uintptr       uintptr_t       up
  intmax        intmax_t         j
  uintmax       uintmax_t       uj

Felix guarantees all the addressing and exact integer aliases exist, even if the C89, C++ or C99 counterparts do not. When the C/C++ counterparts do exist, Felix binds to the same integer type, otherwise it binds to the largest appropriate integer type (for example, if both long and long long exist and are the same size as a pointer, and intptr_t is not defined by C or C++, then Felix choses long long as the alias).

Note that there are no suffices for the special address calculating integer aliases. It is best to use a literal with a long enough type and cast it as required.

Note also that unlike modern C and C++ very long integers without a suffix are still type int. This is because Felix has no idea of the limits of integer types.

+ 1.1.2 Integer literal radix

An integer literal consists of a prefix, numeric form and suffix. The suffix is noted in the tables above. Either the size indicator or signedness indicator 'u' or 'i' can be wholly capitalised (but note that lL is not allowed).

The prefix denotes the radix as in the table below:

  Prefix      Radix    name         Allowed digits
  --------------------------------------------------------
  0b 0B       2        binary       01
  0o 0O       8        octal        01234567
  0d 0D none  10       decimal      0123456789
  0x 0X       16       hexadecimal  0123456789ABCDEFabcdef

Hexadecimal digits which are letters may also be capitalised. Please note that unlike C 077 is decimal 77, it is not octal, you must use 0o77 to get octal!

+ 1.1.3 Underscore separators

The numeric form consists of digits from the table above, with optional underscores inserted for layout purposes. Two underscores in a row are not permitted, one is allowed between digits, or a digit and the prefix or suffix.

+ 1.1.4 Exact rule for literals

let bindigit = ['0'-'1']
let octdigit = ['0'-'7']
let digit = ['0'-'9']
let hexdigit = digit | ['A'-'F'] | ['a'-'f']
let underscore = '_'

let bin_lit  = '0' ('b' | 'B') (underscore? bindigit) +
let oct_lit  = '0' ('o' | 'O') (underscore? octdigit) +
let dec_lit  = ('0' ('d' | 'D'| "d_" | "D_"))? digit (underscore? digit) *
let hex_lit  = '0' ('x' | 'X') (underscore? hexdigit)  +
let fastint_type_suffix = 't'|'T'|'s'|'S'|'i'|'I'|'l'|'L'|'v'|'V'|"ll"|"LL"
let exactint_type_suffix =
    "i8" | "i16" | "i32" | "i64"
  | "u8" | "u16" | "u32" | "u64"
  | "I8" | "I16" | "I32" | "I64"
  | "U8" | "U16" | "U32" | "U64"

let signind = 'u' | 'U'

let suffix =
    '_'? exactint_type_suffix
  | ('_'? fastint_type_suffix)? ('_'? signind)?
  | ('_'? signind)? ('_'? fastint_type_suffix)?

let int_lit = (bin_lit | oct_lit | dec_lit | hex_lit) suffix

+ 1.1.5 Example literals

  // radix
  {
    val i1 = 999;      // decimal
    val i2 = 0b1111;   // binary
    val i3 = 0o7777;   // octal
    val i4 = 0d9999;   // decimal
    val i5 = 0xF1_F2;  // hex
  };
  
  // underscores
  {
    val i1 = 123_456;
    val i2 = 0x_FF_FFu;
  };
  
  // normal signed integers
  {
    val i1 : tiny = 1t;
    val i2 : short = 1s;
    val i3 : int = 1; // default
    val i4 : int = 1i;
    val i5 : long = 1l;
    val i6 : vlong = 1ll;
  };
  
  // normal unsigned integers
  {
    val i1 : utiny = 1tu;
    val i2 : utiny = 1ut; // u may be first or last
    val i3 : ushort = 1su;
    val i4 : uint = 1u; // default
    val i5 : uint = 1iu;
    val i6 : ulong = 1lu;
    val i7 : uvlong = 1vu;
    val i8 : uvlong = 1llu; // alternate
  };
  
  // exact signed integers
  {
    val i1 : int8 = 1i8;
    val i2 : int16 = 1i16;
    val i3 : int32 = 1i32;
    val i4 : int64 = 1i64;
  };
  
  // exact unsigned integers
  {
    val i1 : uint8 = 1u8; // 1iu8 not allowed
    val i2 : uint16 = 1u16;
    val i3 : uint32 = 1u32;
    val i4 : uint64 = 1u64;
  };

+ 1.1.6 Functions

Arithmetic types support the same operations as ISO C99, except that Felix does not support bitwise operations on signed integers (shifts are supported, they're multiplications by powers of two).

Note also, Felix does not provide automatic conversions. All standard integer operations of two arguments require both argument to be the same type, all operations return a value of that type.

Overflow of operations on signed types is undefined behaviour. Operations on unsigned types cannot overflow because they are defined as the modular residual of the underlying mathematical operation on integers.

Thus, operations on exact unsigned integral types are fully deterministic, and operations on signed integral types are also deterministic when they do not overflow.

In the table below the most tightly binding operation groups are given first (with one exception, power). Please note that all operator expressions are transformed by the parser into function calls.

  operator parsing function     meaning
  ------------------------------------------------------------------
                   abs i        absolute value         (signed only)
                   sgn i        sign       (returns int -1, 0 or +1)
                                                       (signed only)
                   succ i       successor
                   pred i       predecessor
                   max(i,j)     maximum
                   min(i,j)     minimum
                   gcd(i,j)     greatest common divisor
                   lcm(i,j)     lowest common multiple
  ------------------------------------------------------------------
  +i       special plus i       identity
  -i       special neg i        negation
  ~i       special bnot i       bitwise compliment   (unsigned only)
  i**j     special pow (i,j)    power
  ------------------------------------------------------------------
  i/j      left    div (i,j)    integer division
  i%j      left    mod (i,j)    modular remainder
  ------------------------------------------------------------------
  i*j      unassoc mul (i,j)    multiplication
  ------------------------------------------------------------------
  i-j      left    sub (i,j)    subtraction
  ------------------------------------------------------------------
  i+j      unassoc add (i,j)    addition
  ------------------------------------------------------------------
  i<<j     left    shl (i,j)    multiply by power of 2
  i>>j     left    shr (i,j)    divide by power of 2
  -----------------------------------------------------------------
  i\&j     left    band (i,j)   bitwise and          (unsigned only)
  ------------------------------------------------------------------
  i\^j     left    bxor (i,j)   bitwise exclusive or (unsigned only)
  ------------------------------------------------------------------
  i\|j     left    bor (i,j)    bitwise or           (unsigned only)
  ------------------------------------------------------------------
  i<j              lt (i,j)     less than             (returns bool)
  i<=j             le (i,j)     less than or equal    (returns bool)
  i>=j             ge (i,j)     greater than or equal (returns bool)
  i>j              gt (i,j)     greater than          (returns bool)
  i==j             eq (i,j)     equal                 (returns bool)
  i!=j             neq (i,j)    not equal             (returns bool)

There two very important and unusual things to note. The first is the weird special grammar of the power operator. Whilst {- a**b} means {-(a**b)}, weirdly {a**-b} means {a^(-b)}. This special grammar supports common mathematical usage.

The second thing to note is that division has a higher precedence than multiplication, and subtraction has a higher precedence than addition. This has no effect on non-overflowing signed integer operations, but it does make a difference for unsigned integer operations and also floating point (which isn't associative).

The reason for this quirk is that Felix uses the same grammar for types as run time expressions, and we require tuple types {a * b * c} to be distinct from {a * (b * c)} and {(a * b) * c} and similarly for {a + b + c}. Because of this operator {*} is not left or right associative, and in particular can't associate with operator {/} so they have to have different precedences.

For a complete and precise understanding of the grammar you must consult the grammar file {lib/grammar/expressions.flxh}.

For a precise understanding of the standard operations on integers you must consult the typeclass specifications in {lib/std/algebraic.flx}.

+ 1.1.6.1 Mixed mode integer operations

Felix by default only supports arithmetic on integers of exactly the same type. To enable mixed mode arithmetic open the MixedInt module.

+ 1.1.7 Procedures

Felix supports the following procedures on integers:

  operator     procedure
  -----------------------------
  ++i;         pre_incr (&i)
  i++;         post_incr (&i)
  --i;         pre_decr (&i)
  i--;         post_decr (&i)
  i+=j;        plus_eq  (&i,j)
  i-=j;        minus_eq (&i,j)
  i*=j;        mul_eq (&i,j)
  i/=j;        div_eq (&i,j)
  i%=j;        mod_eq (&i,j)
  i|=j;        bor_eq (&i,j)
  i^=j;        bxor_eq (&i,j)
  i&=j;        band_eq (&i,j)

Note that the first argument of the procedure is a pointer to the store to be modified.