Numeric types, integers and conversions
Each numeric namespace uses its corresponding TypedArray and explicit conversion rules. Types never promote implicitly.
Types and storage
engine.f64/i8/u8/i16/u16/i32/u32 accept the corresponding standard TypedArrays. Arenas and synchronous scopes expose the same namespaces with alloc/from. Handles expose readonly dtype and length; from/set/toArray retain copy semantics. Each floating namespace now has 73 methods; each signed integer has 37 and each unsigned integer has 35. Unsigned integers have no neg/abs. Integers have no floating div, classification, sum/dot, scans or combination methods; integer division is divTrunc.
The default output is independent; right scalars broadcast where registered, and explicit out may exactly alias same-dtype inputs. Shift/rotate counts are scalar only. Numeric u8 and mask are distinct resident dtypes; array masks are canonical 0/1 Uint8Arrays. Uint8ClampedArray, BigInt arrays, shared/resizable/ detached backing memory and foreign-realm arrays are rejected.
Layouts come from the registry. Maximum byte length is 2^31 - 1; maximum element count is its floor quotient by element size. f64 allocations are eight-byte aligned. Wasm views are refreshed for each access so handles survive memory growth. Engine or arena disposal invalidates owned handles.
f64 behavior
Scalar arguments, storage, intermediate basic operations and results stay f64. They do not pass through Math.fround or a Wasm f32 argument. NaN, zero signs and rounding follow the floating-point contract. Scans remain sequential, initialized from the first element. Every multiply, add and subtract in combinations rounds separately; no implicit FMA is used.
Reductions use the fixed algorithms: compensated sum/dot, sequential product, scaled l2Norm and centered two-pass variance. The f64 accumulator does not widen. Each f64 dot product rounds before summation; no exact or correctly rounded total is promised. Intermediate overflow and underflow follow IEEE: the sum and mean of [MAX_VALUE, MAX_VALUE] may both be Infinity, and computing variance before its square root can overflow stddev. The scaled l2Norm avoids directly squaring extreme inputs.
Empty identities, NaN/infinity handling, ddof and first-extremum rules follow the reduction contract. Tests use exact per-step f64 references where appropriate, and finite-data tolerances plus cancellation, extreme norms and small-variance cases for statistics. These budgets are not whole-domain ULP guarantees.
Integer behavior
add/sub/mul/square/neg/abswrap at the target width;abs(INT_MIN)is INT_MIN. General integer kernels use i128, avoiding a lossy JS floating intermediate for u32 multiplication.addSaturating/subSaturatingclamp to the dtype range for all six integer types.divTruncrounds toward zero. Zero divisors and signedINT_MIN / -1fail before writing.remainderhas the dividend's sign;INT_MIN % -1is zero; a zero divisor fails.- Shift and rotation counts must be finite integers in
0 <= count < bitWidth.shiftRightis arithmetic for signed types and logical for unsigned types.shiftRightLogicalzero-fills and returns the result in the original dtype. mulHighreturns the high half of the signed or unsigned double-width product.popcount/clz/ctzreturn the input dtype; clz(0) and ctz(0) equal the bit width.- Scalars must be finite integers in the target range. JS implicit 32-bit coercion is not used. Clamp bounds are checked and lower must not exceed upper.
- Extremum reductions return JS numbers. Empty reduceMin/reduceMax return the dtype maximum/minimum. argMin/argMax choose the first match, or -1 when empty.
Explicit conversions
Use engine.convert(input, targetType, { mode?, out? }) or its arena counterpart. Exactly 36 directed pairs are registered: f32↔f64, i32↔f32, u8↔f32 and the 30 nonidentity pairs among the six integer dtypes. Same-type copies, other floating pairs and mask/u8 conversion are unsupported. Types restrict targets and runtime validation repeats the check; Rust conversion pairs are generated from the registry.
| Mode | Float to integer | Integer to integer | Float target |
|---|---|---|---|
checked (default) | Truncate toward zero, then reject NaN/infinity/out-of-range results | Check range | IEEE rounding |
saturate | Truncate and clamp; NaN → 0, infinities → endpoints | Clamp | IEEE rounding |
wrap | Reject, including empty input | Wrap to target width | Reject |
Floating conversions may lose precision. f64→f32 can overflow to infinity or underflow to signed zero; integer→f32 can round. Checked failures are found before changing output. Different-dtype input/output byte intervals must not overlap, even at the same starting address. This includes u8/mask comparisons and selection. Read-only inputs may overlap; partial output overlap is always rejected.
Backend checks
Typed/conversion channels use f64 scalars and separate status/result channels. Original f32 entry points continue rejecting wrong call families or dtypes. N-API validates actual array types, lengths and overlap; Wasm validates live allocations, dtype, length, scalar flags and operation signature. Kernels check scalars, shifts, division, masks, clamp, ddof and conversion policy. Integer division and checked conversion include a preflight traversal, including resident calls.
The added numeric types and conversions currently use scalar kernels. Use getCapability(operation, dtype) or getCapability('convert', source, target); simd: 'required' only constrains initialization. See support and compatibility for recorded backend coverage.