Integer numbers follow C-style format with support for decimal, binary (0b), octal (0o) and hex (0x) notations.

The default system integer type (also aliased to INT) is i64. It can be turned into i32 via the only_i32 feature.

Floating-Point Numbers

Floating-point numbers are also supported if not disabled with no_float.

Both decimal and scientific notations can be used.

The default system floating-point type is i64 (also aliased to FLOAT). It can be turned into f32 via the f32_float feature.

Decimal Numbers

When rounding errors cannot be accepted, such as in financial calculations, the decimal feature turns on support for the Decimal type, which is a fixed-precision floating-point number with no rounding errors.

Number Literals

_ separators can be added freely and are ignored within a number – except at the very beginning or right after a decimal point (.).

SampleFormatTypeno_floatno_float + decimal
_123syntax error (improper separator)
123_345, -42decimalINTINTINT
123._456syntax error (improper separator)
123_456.78_9normal floating-pointFLOATsyntax errorDecimal
-42.ending with decimal pointFLOATsyntax errorDecimal
123_456_.789e-10scientific notationFLOATsyntax errorDecimal
.456syntax error (missing leading 0)
123.456e_10syntax error (improper separator)
123.e-10syntax error (missing decimal 0)

Warning – No Implicit Type Conversions

Unlike most C-like languages, Rhai does not provide implicit type conversions between different numeric types.

For example, a u8 is never implicitly converted to i64 when used as a parameter in a function call or as a comparison operand. f32 is never implicitly converted to f64.

This is exactly the same as Rust where all numeric types are distinct. Rhai is written in Rust afterall.

Therefore, care must be taken especially with regards to integer variables pushed inside a custom Scope that they are of the intended type.

It is extremely easy to mess up numeric types since the Rust default integer type is i32 while for Rhai it is i64 (without only_i32).

fn main() {
use rhai::{Engine, Scope, INT};

let engine = Engine::new();

let mut scope = Scope::new();

scope.push("r", 42);            // 'r' is i32 (Rust default integer type)
scope.push("x", 42_u8);         // 'x' is u8
scope.push("y", 42_i64);        // 'y' is i64
scope.push("z", 42 as INT);     // 'z' is i64 (or i32 under 'only_i32')
scope.push("f", 42.0_f32);      // 'f' is f32

// Rhai integers are i64 (i32 under 'only_i32')
engine.eval::<String>("type_of(42)")? == "i64";

// false - i32 is never equal to i64
engine.eval_with_scope::<bool>(&mut scope, "r == 42")?;

// false - u8 is never equal to i64
engine.eval_with_scope::<bool>(&mut scope, "x == 42")?;

// true - i64 is equal to i64
engine.eval_with_scope::<bool>(&mut scope, "y == 42")?;

// true - INT is i64
engine.eval_with_scope::<bool>(&mut scope, "z == 42")?;

// false - f32 is never equal to f64
engine.eval_with_scope::<bool>(&mut scope, "f == 42.0")?;

Floating-Point vs. Decimal

Decimal (enabled via the decimal feature) represents a fixed-precision floating-point number which is popular with financial calculations and other usage scenarios where round-off errors are not acceptable.

Decimal takes up more space (16 bytes) than a standard FLOAT (4-8 bytes) and is much slower in calculations due to the lack of CPU hardware support. Use it only when necessary.

For most situations, the standard floating-point number type FLOAT (f64 or f32 with f32_float) is enough and is faster than Decimal.

It is possible to use both FLOAT and Decimal together with just the decimal feature – use parse_decimal or to_decimal to create a Decimal value.

Use no_float and decimal Together

When both no_float and decimal features are turned on, Decimal replaces the standard floating-point type.

Floating-point number literals in scripts parse to Decimal values.