You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While writing out the emulation decomposition for quantizeLinear, I lacked rounding functions in WebNN, particularly the default IEEE standard round-tied-halves-to-nearest-even. There's not a nice way to simulate this missing function, and it's a basic primitive that belongs in a library anyway. So I propose filling in some gaps ⭐:
Add roundEven() which is the IEEE standard's default, and relevant ML libraries support it (see support below).
Document trunc via emulation at least. I originally thought it worth adding, but I haven't actually seen it in models, and evidently only DML ROUND and PyTorch torch.trunc have it, not TFLite/CoreML/ONNX. There are emulations below.
Modes
Rounding mode
WebNN
IEEE
C++ equivalent
JS equivalent
RHE nearest int, halves to even ⭐ (banker's rounding)
Some of these modes are not generally useful, but they are very useful within their domain. For example, RHD is used often in graphics when rasterizing triangles or resampling images rather than RHE or other modes to avoid staggered/asymmetric pattern artifacts (WebNN's resample uses RHD internally). I'm not proposing those though.
Javascript's Math.round is oddly aberrant, being neither RHE or RHAZ (the two most common IEEE nearest modes). So beware when using it as a baseline reference. Also I'm using "up" (higher value toward positive infinity) and "down" (lower value toward negative infinity) like C's usage, not Java's bidirectional RoundingMode where up and down mean away from zero and toward zero.
A backend can emulate RHE if it has modulus/remainder and trunc, as x - std::remainder(x, static_cast<T>(1)) (source) (even though WebNN itself lacks both those operators).
Another odd approach that evidently works for float32 (but not as-is for float16/float64) uses a few condition checks with addition and subtraction, but that ends up being a clumsy construction in WebNN.
Naming: Why not just "round"? round is woefully ambiguous as evidenced from the differences between C++ std::round (RHAZ) vs Javascript/Java Math.round (RHU) vs round in others (RHE) 😕. roundEven (like C23 roundeven) is directly clear in the name.
The text was updated successfully, but these errors were encountered:
Summary
While writing out the emulation decomposition for
quantizeLinear
, I lacked rounding functions in WebNN, particularly the default IEEE standard round-tied-halves-to-nearest-even. There's not a nice way to simulate this missing function, and it's a basic primitive that belongs in a library anyway. So I propose filling in some gaps ⭐:roundEven()
which is the IEEE standard's default, and relevant ML libraries support it (see support below).trunc
via emulation at least. I originally thought it worth adding, but I haven't actually seen it in models, and evidently only DML ROUND and PyTorch torch.trunc have it, not TFLite/CoreML/ONNX. There are emulations below.Modes
RHE
nearest int, halves to even ⭐ (banker's rounding)The recommended IEEE float default
std::nearbyint FE_TONEAREST
RHAZ
nearest int, halves away from zerofloor(abs(x) + 0.5) * sign(x)
RHTZ
nearest int, halves toward zeroceil(abs(x) - 0.5) * sign(x)
RHU
nearest int, halves upfloor(x + 0.5)
RHD
nearest int, halves downceil(x - 0.5)
RAZ
away from zeroceil(abs(x) * sign(x))
RTZ
toward zero (trunc) ⭐floor(abs(x) * sign(x))
std::nearbyint FE_TOWARDZERO
RU
up to positive infinity (ceil)ceil(x)
std::nearbyint FE_UPWARD
RD
down to negative infinity (floor)floor(x)
std::nearbyint FE_DOWNWARD
Note
RHD
is used often in graphics when rasterizing triangles or resampling images rather thanRHE
or other modes to avoid staggered/asymmetric pattern artifacts (WebNN'sresample
usesRHD
internally). I'm not proposing those though.Math.round
is oddly aberrant, being neitherRHE
orRHAZ
(the two most common IEEE nearest modes). So beware when using it as a baseline reference. Also I'm using "up" (higher value toward positive infinity) and "down" (lower value toward negative infinity) like C's usage, not Java's bidirectional RoundingMode where up and down mean away from zero and toward zero.Library support
RHE
:RHE
from the docs, but its single number example of 0.5 implies that it is, and by default, I presume the IEEE default.RTZ
:Examples
RHE
nearest int, halves to evenRHAZ
nearest int, halves away from zeroRHTZ
nearest int, halves toward zeroRHU
nearest int, halves upRHD
nearest int, halves downRAZ
away from zeroRTZ
toward zero (trunc)RU
up to positive infinity (ceil)RD
down to negative infinity (floor)Emulation
RHE (roundEven)
A backend can emulate
RHE
if it hasmodulus
/remainder
andtrunc
, asx - std::remainder(x, static_cast<T>(1))
(source) (even though WebNN itself lacks both those operators).Another odd approach that evidently works for float32 (but not as-is for float16/float64) uses a few condition checks with addition and subtraction, but that ends up being a clumsy construction in WebNN.
RTZ (trunc)
There's also an approximation using a round-trip
cast
(float -> int -> float), but that's lossy with large numbers.Models
API
Naming: Why not just "round"?
round
is woefully ambiguous as evidenced from the differences between C++std::round
(RHAZ) vs Javascript/JavaMath.round
(RHU) vsround
in others (RHE) 😕.roundEven
(like C23 roundeven) is directly clear in the name.The text was updated successfully, but these errors were encountered: