Skip to content

[Bug]: OpenVINO CPU saturates uint8 Mul overflow instead of wrapping #35646

@ALinrunrun

Description

@ALinrunrun

OpenVINO Version

2026.1.0

Operating System

Other (Please specify in description)

Device used for inference

CPU

Framework

ONNX

Model used

Minimal ONNX model generated by the reproducer script.

Issue description

OpenVINO CPU produces saturated results for uint8 ONNX Mul overflow cases.

For a valid opset-14 ONNX model, ONNX Runtime returns the expected fixed-width uint8 wraparound result, while OpenVINO CPU clamps every overflowed result to 255.

OS: Ubuntu 24.04.2 LTS / Linux x86_64

Step-by-step reproduction

#!/usr/bin/env python3
import sys
import numpy as np
import onnx
import onnxruntime as ort
import openvino as ov
from onnx import TensorProto, helper

a = np.array([200, 100, 50, 10], dtype=np.uint8)
b = np.array([200, 3, 6, 30], dtype=np.uint8)

wrap_ref = ((a.astype(np.int32) * b.astype(np.int32)) % 256).astype(np.uint8)
sat_ref = np.clip(a.astype(np.int32) * b.astype(np.int32), 0, 255).astype(np.uint8)

A = helper.make_tensor_value_info("A", TensorProto.UINT8, [4])
B = helper.make_tensor_value_info("B", TensorProto.UINT8, [4])
Y = helper.make_tensor_value_info("Y", TensorProto.UINT8, [4])

node = helper.make_node("Mul", ["A", "B"], ["Y"])
graph = helper.make_graph([node], "uint8_mul_overflow", [A, B], [Y])
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 14)])
model.ir_version = 8
onnx.checker.check_model(model)
mb = model.SerializeToString()
feed = {"A": a, "B": b}

ort_out = ort.InferenceSession(
    mb,
    providers=["CPUExecutionProvider"],
).run(None, feed)[0]

core = ov.Core()
compiled = core.compile_model(core.read_model(mb, b""), "CPU")
ov_out = compiled(feed)[compiled.output(0)]

print(f"a        : {a}")
print(f"b        : {b}")
print(f"a*b      : {a.astype(np.int32) * b.astype(np.int32)}")
print(f"ORT out  : {ort_out}")
print(f"OV out   : {ov_out}")
print(f"wrap ref : {wrap_ref}")
print(f"sat  ref : {sat_ref}")

ort_wrap = np.array_equal(ort_out, wrap_ref)
ov_wrap = np.array_equal(ov_out, wrap_ref)
ov_sat = np.array_equal(ov_out, sat_ref)

print(f"\nORT matches wrap? {ort_wrap}")
print(f"OV matches wrap?  {ov_wrap}")
print(f"OV matches sat?   {ov_sat}")

if ort_wrap and ov_sat and not ov_wrap:
    print("\nBUG REPRODUCED: OpenVINO uint8 Mul saturates instead of wrapping.")
    sys.exit(0)

print("\nNOT REPRODUCED")
sys.exit(1)

Relevant log output

Expected:
OpenVINO CPU should produce the same uint8 Mul overflow result as ONNX Runtime for this valid opset-14 ONNX model.

Actual:
a        : [200 100  50  10]
b        : [200   3   6  30]
a*b      : [40000   300   300   300]
ORT out  : [64 44 44 44]
OV out   : [255 255 255 255]
wrap ref : [64 44 44 44]
sat  ref : [255 255 255 255]

ORT matches wrap? True
OV matches wrap?  False
OV matches sat?   True

BUG REPRODUCED: OpenVINO uint8 Mul saturates instead of wrapping.

Issue submission checklist

  • I'm reporting an issue. It's not a question.
  • I checked the problem with the documentation, FAQ, open issues, Stack Overflow, etc., and have not found a solution.
  • There is reproducer code and related data files such as images, videos, models, etc.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions