Taichi supports common numerical data types. Each type is denoted as
a character indicating its category and a number of precision bits, e.g., i32
and f64
.
The category can be one of:
i
for signed integers, e.g. 233, -666u
for unsigned integers, e.g. 233, 666f
for floating point numbers, e.g. 2.33, 1e-4
The digital number can be one of:
8
16
32
64
It represents how many bits are used in storing the data. The larger the bit number, the higher the precision is.
For example, the two most commonly used types:
i32
represents a 32-bit signed integer.f32
represents a 32-bit floating pointer number.
Currently, supported basic types in Taichi are
- int8
ti.i8
- int16
ti.i16
- int32
ti.i32
- int64
ti.i64
- uint8
ti.u8
- uint16
ti.u16
- uint32
ti.u32
- uint64
ti.u64
- float32
ti.f32
- float64
ti.f64
Note
Supported types on each backend:
type | CPU/CUDA | OpenGL | Metal |
---|---|---|---|
i8 | OK | N/A | OK |
i16 | OK | N/A | OK |
i32 | OK | OK | OK |
i64 | OK | EXT | N/A |
u8 | OK | N/A | OK |
u16 | OK | N/A | OK |
u32 | OK | N/A | OK |
u64 | OK | N/A | N/A |
f32 | OK | OK | OK |
f64 | OK | OK | N/A |
(OK: supported, EXT: require extension, N/A: not available)
Note
Boolean types are represented using ti.i32
.
Binary operations on different types will give you a promoted type, following the C programming language convention, e.g.:
i32 + f32 = f32
(integer + float = float)i32 + i64 = i64
(less-bits + more-bits = more-bits)
Basically it will try to choose the more precise type to contain the result value.
By default, all numerical literals have 32-bit precisions.
For example, 42
has type ti.i32
and 3.14
has type ti.f32
.
Default integer and float-point precisions (default_ip
and default_fp
) can be specified when initializing Taichi:
ti.init(default_fp=ti.f32)
ti.init(default_fp=ti.f64)
ti.init(default_ip=ti.i32)
ti.init(default_ip=ti.i64)
Also note that you may use float
or int
in type definitions as aliases
for default precisions, e.g.:
ti.init(default_ip=ti.i64, default_fp=ti.f32)
x = ti.var(float, 5)
y = ti.var(int, 5)
# is equivalent to:
x = ti.var(ti.f32, 5)
y = ti.var(ti.i64, 5)
def func(a: float) -> int:
...
# is equivalent to:
def func(a: ti.f32) -> ti.i64:
...
Warning
The type of a variable is determinated on it's initialization.
When a low-precision variable is assigned to a high-precision variable, it will be implicitly promoted to the wide type and no warning will be raised:
a = 1.7
a = 1
print(a) # 1.0
When a high-precision variable is assigned to a low-precision type, it will be implicitly down-cast into the low-precision type and Taichi will raise a warning:
a = 1
a = 1.7
print(a) # 1
You may use ti.cast
to explicitly cast scalar values between different types:
a = 1.7
b = ti.cast(a, ti.i32) # 1
c = ti.cast(b, ti.f32) # 1.0
Equivalently, use int()
and float()
to convert values to float-point or
integer types of default precisions:
a = 1.7
b = int(a) # 1
c = float(a) # 1.0
Type casts applied to vectors/matrices are element-wise:
u = ti.Vector([2.3, 4.7])
v = int(u) # ti.Vector([2, 4])
# If you are using ti.i32 as default_ip, this is equivalent to:
v = ti.cast(u, ti.i32) # ti.Vector([2, 4])
Use ti.bit_cast
to bit-cast a value into another data type. The underlying bits will be preserved in this cast.
The new type must have the same width as the the old type.
For example, bit-casting i32
to f64
is not allowed. Use this operation with caution.
For people from C++, ``ti.bit_cast`` is equivalent to ``reinterpret_cast``.