# Column datatypes A scalar column holds one value per row, and H5Col defines four families for it: numeric columns, fixed-length strings, booleans, and categoricals. Each family maps to plain HDF5 datatypes, so files remain readable everywhere. What the convention adds is precise semantics on top. Variable-length values per row are the job of [list columns](list-columns.md), which have their own chapter. Every family is declared through the same object, a {class}`~h5col.ColumnSpec`, whose fields cover identity (`name`, `description`), the datatype, storage (`chunks`, `filters` — see [filters](filters.md)), and value semantics (`fill_value`, `valid_min`, `valid_max`, `units`, `units_vocabulary`). ## Numeric columns The convention's core numeric set covers: `int8` through `int64`, their unsigned counterparts, `float32`, and `float64`. They can be declared in several flavors: `np.float64`, `"float64"`, or `np.dtype("` returns labels, with `None` where the code is the fill. The raw codes remain available as {attr}`Column.codes `, the labels as {attr}`Column.categories `, and an optional `ordered` flag (for ordinal categories) round-trips through the spec and {attr}`Column.ordered `. The default fill code is `-1` for signed code dtypes (and the type maximum for unsigned), which cannot collide with a valid code as long as the code type leaves room for it. A fill that would collide, an unsigned code type fully saturated by its categories, is rejected at creation, and an explicit categorical fill must likewise lie outside `[0, ncategories)`. Categoricals earn their keep twice: in storage, where a repeated 20-byte label costs one byte per row, and in queries, where equality predicates compare labels (`field("payment_type") == "Cash"`) and a [bitmap index](indexes.md) answers them exactly without touching the column data.