github ↗
docs menu

Module Nx_array.Dtype

Element formats.

Element formats.

A dtype ('v, 's) t names a storage format 's whose elements OCaml reads and writes as values of type 'v. Operands of one format share a type, so a signature states which operands share one and the compiler checks every typed call. Where Bigarray has the format, 's is Bigarray's element type, so arrays and bigarrays exchange bytes with their kinds checked by the type.

A dtype's facts (its name, bits, kind and float_format) are rows of one table indexed by its code, which the C header nx_dtype.h holds too. Every store of a float into a dtype follows one rule, stated in Stores.

An element wider than a byte is stored little-endian, the byte order of every host and device this library runs on: its low byte comes first, and a narrowing bitcast reads an element's low bits first.

Storage formats

The second parameter of a dtype. Bigarray's element types name the formats Bigarray has; the others are types of this module with no values. A dtype's name counts the bits of a whole element, Bigarray's element types one component's: Complex64's elements are complex32_elt.

#type float64_elt = Stdlib.Bigarray.float64_elt

IEEE 754 binary64.

#type float32_elt = Stdlib.Bigarray.float32_elt

IEEE 754 binary32.

#type float16_elt = Stdlib.Bigarray.float16_elt

IEEE 754 binary16.

#type bfloat16_elt = |

bfloat16: binary32's sign, 8 exponent bits and the top 7 fraction bits.

#type float8_e4m3fn_elt = |

float8 E4M3FN: 4 exponent bits, 3 fraction bits, exponent bias 7, no infinity; S.1111.111 is NaN. Its largest finite value is 448.

#type float8_e5m2_elt = |

float8 E5M2: 5 exponent bits, 2 fraction bits, exponent bias 15, with infinities and NaNs as in IEEE 754. Its largest finite value is 57344.

#type float4_e2m1fn_elt = |

float4 E2M1FN: 2 exponent bits, 1 fraction bit, exponent bias 1, no infinity and no NaN. Its values are ±{0, 0.5, 1, 1.5, 2, 3, 4, 6}.

#type int64_elt = Stdlib.Bigarray.int64_elt

Signed 64-bit integers.

#type uint64_elt = |

Unsigned 64-bit integers.

#type int32_elt = Stdlib.Bigarray.int32_elt

Signed 32-bit integers.

#type uint32_elt = |

Unsigned 32-bit integers.

#type int16_signed_elt = Stdlib.Bigarray.int16_signed_elt

Signed 16-bit integers.

#type int16_unsigned_elt = Stdlib.Bigarray.int16_unsigned_elt

Unsigned 16-bit integers.

#type int8_signed_elt = Stdlib.Bigarray.int8_signed_elt

Signed 8-bit integers.

#type int8_unsigned_elt = Stdlib.Bigarray.int8_unsigned_elt

Unsigned 8-bit integers.

#type int4_elt = |

Signed 4-bit integers, two's complement.

#type uint4_elt = |

Unsigned 4-bit integers.

#type complex64_elt = Stdlib.Bigarray.complex64_elt

Complex numbers of two binary64 components, real part first.

#type complex32_elt = Stdlib.Bigarray.complex32_elt

Complex numbers of two binary32 components, real part first.

#type bool_elt = |

Booleans, one to a byte: true iff the byte is not zero.

#type bit_elt = |

Booleans, one to a bit.

Dtypes

#type ('v, 's) t =
  1. #| Float64 : (float, float64_elt) t
  2. #| Float32 : (float, float32_elt) t
  3. #| Float16 : (float, float16_elt) t
  4. #| Bfloat16 : (float, bfloat16_elt) t
  5. #| Float8_e4m3fn : (float, float8_e4m3fn_elt) t
  6. #| Float8_e5m2 : (float, float8_e5m2_elt) t
  7. #| Float4_e2m1fn : (float, float4_e2m1fn_elt) t
  8. #| Int64 : (int64, int64_elt) t
  9. #| Uint64 : (int64, uint64_elt) t
  10. #| Int32 : (int32, int32_elt) t
  11. #| Uint32 : (int32, uint32_elt) t
  12. #| Int16 : (int, int16_signed_elt) t
  13. #| Uint16 : (int, int16_unsigned_elt) t
  14. #| Int8 : (int, int8_signed_elt) t
  15. #| Uint8 : (int, int8_unsigned_elt) t
  16. #| Int4 : (int, int4_elt) t
  17. #| Uint4 : (int, uint4_elt) t
  18. #| Complex128 : (Stdlib.Complex.t, complex64_elt) t
  19. #| Complex64 : (Stdlib.Complex.t, complex32_elt) t
  20. #| Bool : (bool, bool_elt) t
  21. #| Bit : (bool, bit_elt) t

The type for dtypes. An element's OCaml type depends only on its format's width: float for every float format, int up to 16 bits, int32 and int64 for 32 and 64 bits, Complex.t and bool.

An unsigned format carries its bits in the type of its width, and the dtype says how to read them: pp_value prints them unsigned, and the Uint32 value -1l is 4294967295.

#type any =
  1. #| Any : ('v, 's) t -> any

The type for dtypes chosen at run time. equal_witness recovers the static type. An any is its dtype's own value, so making one allocates nothing.

#val all : any list

all is every dtype, in code order.

Facts

#val code : ('v, 's) t -> int

code dt is dt's code: its index in all and the constant NX_<NAME> of nx_dtype.h, as NX_FLOAT32 for Float32.

#val of_code : int -> any option

of_code c is the dtype whose code is c, if any: None unless 0 <= c < List.length all. It allocates nothing.

#val bits : ('v, 's) t -> int

bits dt is the width of one element of dt in bits: 1 for Bit, 4 for the 4-bit formats, 8 for Bool, up to 128 for Complex128.

#val bytes : ('v, 's) t -> int -> int

bytes dt n is the number of bytes n elements of dt fill, ⌈n · bits dt / 8⌉.

Raises Invalid_argument if n < 0 or the number does not fit in an int.

#val name : ('v, 's) t -> string

name dt is dt's constructor name in lower case, as "float32" or "float4_e2m1fn".

#val of_name : string -> any option

of_name s is the dtype whose name is s, if any.

#val pp : Stdlib.Format.formatter -> ('v, 's) t -> unit

pp formats a dtype's name.

Kinds

#type 'v kind =
  1. #| Float : float kind
  2. #| Complex : Stdlib.Complex.t kind
  3. #| Signed : 'v kind
  4. #| Unsigned : 'v kind
  5. #| Boolean : bool kind

The type for kinds of number. 'v is the value type of the kind's dtypes where the kind fixes one: integers read as int, int32 or int64 by width.

#val kind : ('v, 's) t -> 'v kind

kind dt is the kind of number dt holds. Matching it learns the value type: in the Float arm of match kind dt with Float -> …, values of dt are floats.

#val is : 'k kind -> ('v, 's) t -> bool

is k dt is true iff kind dt is k.

Equality

#val equal : ('v, 's) t -> ('w, 'r) t -> bool

equal dt dt' is true iff dt and dt' are the same dtype.

#val equal_witness : ('v, 's) t -> ('w, 'r) t -> (('v, 's) t, ('w, 'r) t) Stdlib.Type.eq option

equal_witness dt dt' is Some Equal iff equal dt dt'.

Bigarray kinds

#val of_bigarray_kind : ('v, 's) Stdlib.Bigarray.kind -> ('v, 's) t option

of_bigarray_kind k is the dtype that stores k's elements, if one does: None for Char, Int and Nativeint.

#val to_bigarray_kind : ('v, 's) t -> ('v, 's) Stdlib.Bigarray.kind option

to_bigarray_kind dt is the kind of dt's elements, if Bigarray has their format: None for Bfloat16, the float8 and float4 dtypes, Int4, Uint4, Uint32, Uint64, Bool and Bit.

Values

#val zero : ('v, 's) t -> 'v

zero dt is dt's additive identity: 0, and false for booleans.

#val one : ('v, 's) t -> 'v

one dt is dt's multiplicative identity: 1, and true for booleans.

#val min_value : ('v, 's) t -> 'v

min_value dt is dt's least value: neg_infinity for a float format with infinities, -448. for Float8_e4m3fn and -6. for Float4_e2m1fn, and false for booleans.

Raises Invalid_argument if dt is complex.

#val max_value : ('v, 's) t -> 'v

max_value dt is dt's greatest value: infinity for a float format with infinities, 448. for Float8_e4m3fn and 6. for Float4_e2m1fn, and true for booleans. An unsigned format read as int gives its value, as 15, 255 and 65535; Uint32 and Uint64 give the value with every bit set, -1l and -1L.

Raises Invalid_argument if dt is complex.

#val holds : ('v, 's) t -> 'v -> bool

holds dt x is false iff x is an int below min_value dt or above max_value dt: the only values a store into dt refuses. It allocates nothing.

#val exact : ('v, 's) t -> ('w, 'r) t -> bool

exact a b is true iff every value of a is a value of b, so that a store of one into b (Stores) gives the same number, the same infinity or a NaN. A boolean is 0 or 1, which every dtype holds, and only a boolean holds a boolean. An integer is exact into an integer that holds its range, and into a float whose significand, float_format's fraction_bits and one, holds its magnitude: int8 into bfloat16, int16 into float32, int32 into float64, int64 into none. A float is exact into a float of at least its precision, range and least magnitude that has an infinity and a NaN where it does: float16 and bfloat16 into float32, the 8-bit floats into float16. A complex number is exact only into a complex dtype, and a real into a complex one whose component it is exact into. It allocates nothing.

#val pp_value : ('v, 's) t -> Stdlib.Format.formatter -> 'v -> unit

pp_value dt formats a value of dt:

  • a finite float as the shortest decimal that rounds to the same value in dt's format, with its exponent unbounded above, and of those the nearest to it: 0.1 for a Float32 0.1, 65500 for a Float16 65504.. Past the largest finite value only the next half step rounds to it, so a Float8_e4m3fn 448. prints as 450, never as a larger decimal a store would saturate to it. It is written positionally for magnitudes from 10-4 up to 1016, as 0.01563, and as d.ddde±XX outside, as 1.59e+29, with the same digits on every system. nan, inf and -inf for the others;
  • an integer in decimal, an unsigned one unsigned;
  • a complex number as its two components, so formatted, in the form re+imi, or re-imi when the imaginary part is negative, as 0.5-2i;
  • a boolean as true or false.

Raises Invalid_argument if a finite float, or a complex number's component, is not a value of dt's format, as 0.1 is not of Float32: of_float gives the value a store holds.

Stores

Every store of a float into a dtype, a kernel's included, follows one rule:

  • A finite x rounds once, ties to even, to the nearest value of the format with its exponent range unbounded above. Below the least normal magnitude the result is a subnormal or a zero of x's sign.
  • Past the largest finite value, a rounded result or an infinity is the infinity of its sign in the formats that have one, Float64, Float32, Float16, Bfloat16 and Float8_e5m2, and saturates in those that have none: ±448 in Float8_e4m3fn and ±6 in Float4_e2m1fn.
  • NaN is a NaN of the format, by the NaN rule below. Float8_e4m3fn has one NaN of each sign, S.1111.111, and Float4_e2m1fn none: NaN stores there as +0.. of_float gives the quiet NaN of x's sign that keeps the top bits of its payload, as many as the format's fraction holds below its quiet bit, the fraction's top.
  • Integers truncate toward zero and saturate to their range, signed and unsigned alike; NaN stores as 0.
  • Complex numbers store x as their real part, rounded to their component's format, and a zero imaginary part. Booleans store x <> 0., true for NaN.

The NaN rule holds for every NaN result, a store's and an operation's: its sign and payload are unspecified, and it is signalling only where an operand is a signalling NaN, so a quiet NaN stays quiet. Moves of elements and the operations that act on the sign bit alone keep a NaN's bits. This departs from IEEE 754-2019 (§6.2.1, §7.2), which has every operation that computes quiet a signalling NaN: no operation here raises or reads an exception flag, so a signalling NaN computes as a quiet one.

#val of_float : ('v, 's) t -> float -> 'v

of_float dt x is the value a store of x into dt holds (Stores).

So of_float Float16 0.1 is 0x1.998p-4, the binary16 nearest to 0.1; 65519. stores in Float16 as 65504. and 65520. as infinity; and 61440., halfway past Float8_e5m2's largest finite value 57344., stores as infinity.

The rule holds in the floating-point environment's default, which rounds to nearest and keeps subnormals. Under another rounding mode, Float32 stores round that way. Under flush-to-zero, Float32 and Bfloat16 stores of subnormal magnitudes give zero, and stores into Float16 and the formats of a byte or less give the same bits.

Float formats

#type float_format = {
  1. #exponent_bits : int;
    (*

    The width of the exponent field.

    *)
  2. #fraction_bits : int;
    (*

    The width of the fraction field, without the implicit bit.

    *)
  3. #infinities : bool;
    (*

    Whether the format has infinities.

    *)
  4. #nans : bool;
    (*

    Whether the format has NaNs.

    *)
  5. #epsilon : float;
    (*

    The gap between 1. and the next value, 2{^-fraction_bits}.

    *)
  6. #min_normal : float;
    (*

    The least positive normal value.

    *)
  7. #max_finite : float;
    (*

    The largest finite value.

    *)
}

The type for the facts of a float format.

#val float_format : (float, 's) t -> float_format

float_format dt is the facts of dt's format.