- An integer literal is written as a series of one or more base-10 digits (
0-9) without leading zeros. The default type is
- A floating-point literal is written as a series of two or more base-10 digits (
0-9) with a point (
.) somewhere in the middle, indicating the decimal point. The default type is
All numeric types allow underscores at any place in the middle. This is useful to write large numbers in a more readable way:
100000 can be written as
p! 1, typeof(1) p! 1.0, typeof(1.0) p! 100_000, typeof(100_000) p! 100_000.0, typeof(100_000.0)
Float values print with a decimal point. Integer values don't.
There are quite a few more numeric types, but most of them are intended only for special use cases such as binary protocols, specific numeric algorithms, and performance optimization. You probably don't need them for everyday programs.
Equality and Comparison¶
Numbers of the same numerical value are considered equal regarding the equality operator
==, independent of their type.
p! 1 == 1, 1 == 2, 1.0 == 1, -2000.0 == -2000
Besides the equality operator, there are also comparison operators. They determine the relationship between two values. As with equality, comparability is also independent of type.
p! 2 > 1, 1 >= 1, 1 < 2, 1 <= 2
The universal comparison operator is
<=>, also called Spaceship operator for its appearance. It compares its operands and returns a value that is either zero (both operands are equal),
a positive value (the first operand is bigger), or a negative value (the second operand is bigger). It combines the behaviour of all other comparison operators.
p! 1 <=> 1, 2 <=> 1, 1 <=> 2
Basic arithmetic operations can be performed with operators. Most operators are binary (i.e. two operands), and written in infix notation (i.e. between the operands). Some operators are unary (i.e. one operand), and written in prefix notation (i.e. before the operand). The value of the expression is the result of the operation.
p! 1 + 1, # addition 1 - 1, # subtraction 2 * 3, # multiplication 2 ** 4, # exponentiation 2 / 3, # division 2 // 3, # floor division 3 % 2, # modulus -1 # negation (unary)
As you can see, the result of most of these operations between integer operands is also an integer value.
The division operator (
/) is an exception. It always returns a float value. The floor division operator (
//) however returns an integer value, but it's obviously reduced to integer precision.
An operation between integer and float operands always returns a float value. Otherwise, the return type is usually the type of the first operand.
A full list of operators is available in the Operator reference.
When several operators are combined, the question arises in which order are they executed. In math, there are several rules, like multiplication and division taking precedence over addition and subtraction. Crystal operators implement these precedence rules.
A tool to structure operations are parentheses. An operator expression in parentheses always takes precedence over external operators.
p! 4 + 5 * 2, (4 + 5) * 2
All the precedence rules are detailed in the the Operator reference.
Some less common math operations are not operators, but named methods.
p! -5.abs, # absolute value 4.3.round, # round to nearest integer 5.even?, # odd/even check 10.gcd(16) # greatest common divisor
A full list of numerical methods is available in the Number API docs (also check subtypes).
Some arithmetic methods are not defined on the number types directly but in the
p! Math.cos(1), # cosine Math.sin(1), # sine Math.tan(1), # tangent Math.log(42), # natural logarithm Math.log10(312), # logarithm to base 10 Math.log(312, 5), # logarithm to base 5 Math.sqrt(9) # square root
A full list of math methods is available in the Math API docs.
Some mathematical constants are available as constants of the
p! Math::E, # Euler's number Math::TAU, # Full circle constant (2 * PI) Math::PI # Archimedes' constant (TAU / 2)