Tip: Bit-fields

Integers can also be conveniently manipulated as bit-fields.

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

Tip: Notations

Both decimal and scientific notations can be used to represent floating-point numbers.

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

The default system floating-point type is f64 (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 (.).

SampleFormatValue typeno_floatno_float + decimal
_123improper separator
123_345, -42decimalINTINTINT
123._456improper separator
123_456.78_9normal floating-pointFLOATsyntax errorDecimal
-42.ending with decimal pointFLOATsyntax errorDecimal
123_456_.789e-10scientific notationFLOATsyntax errorDecimal
.456missing leading 0
123.456e_10improper separator
123.e-10missing 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.


Integer variables pushed inside a custom Scope must be the correct type.

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

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

Tip: no_float + decimal

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.

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.