<!-- llms-explorer concept facts · https://llms-explorer.com/tree/mlx-mixed-fp16-and-bf16-type-promotion-in-arithm/ · pack 2026-10-05 · ~675 tokens -->

# MLX mixed fp16 and bf16 type promotion in arithmetic

> MLX's promotion table gives float32 for float16 combined with bfloat16, in either operand order.

Parent: [Mac local LLMs: MLX kernels, numerics and internals](https://llms-explorer.com/tree/mac-local-llms-mlx-kernels-numerics-and-internals/) · 1 facets · 12 facts · page: https://llms-explorer.com/tree/mlx-mixed-fp16-and-bf16-type-promotion-in-arithm/

## Facts

- MLX's promotion table gives float32 for float16 combined with bfloat16, in either operand order. — [source](https://raw.githubusercontent.com/ml-explore/mlx/main/mlx/dtype.cpp)
- The promotion table in `mlx/dtype.cpp` is documented in a source comment as following JAX type promotion rules. — [source](https://raw.githubusercontent.com/ml-explore/mlx/main/mlx/dtype.cpp)
- Float16 combined with float16 gives float16, and bfloat16 combined with bfloat16 gives bfloat16. — [source](https://raw.githubusercontent.com/ml-explore/mlx/main/mlx/dtype.cpp)
- Float16 or bfloat16 combined with any bool, uint8 to uint64, or int8 to int64 type keeps the half type. — [source](https://raw.githubusercontent.com/ml-explore/mlx/main/mlx/dtype.cpp)
- Float16 or bfloat16 combined with float32 gives float32. — [source](https://raw.githubusercontent.com/ml-explore/mlx/main/mlx/dtype.cpp)
- `add` computes `promote_types(a.dtype(), b.dtype())` and applies `astype` to both operands before broadcasting, so the primitive runs at the promoted type. — [source](https://raw.githubusercontent.com/ml-explore/mlx/main/mlx/ops.cpp)
- `subtract` uses the same `promote_types` pattern as `add`. — [source](https://raw.githubusercontent.com/ml-explore/mlx/main/mlx/ops.cpp)
- `result_type` over a list of arrays folds `promote_types` starting from bool, so n-ary ops promote the same way. — [source](https://raw.githubusercontent.com/ml-explore/mlx/main/mlx/utils.cpp)
- `TypeToDtype<double>` converts to float32 in `mlx/dtype.cpp`. — [source](https://raw.githubusercontent.com/ml-explore/mlx/main/mlx/dtype.cpp)
- MLX's NumPy interop page says NumPy has no bfloat16, so bfloat16 arrays must be cast to float16 or float32 before `np.array`. — [source](https://ml-explore.github.io/mlx/build/html/usage/numpy.html)
- MLX maintainers declined adding fp8 dtypes in a December 2024 reply, citing that bf16 is already emulated on older machines and fp8 would be slower than fp32 and add library footprint. — [source](https://github.com/ml-explore/mlx/issues/1670)
- In the Gemma 4 float16 guard, keeping `std_bias` in bf16 against fp16 hidden states makes the standardize subtraction run in float32, which avoids the fp16 overflow in that op but changes the output dtype to float32. — source: `asserted`
