Skip to content

Fix undefined behavior casting non-finite / out-of-range float to int in LinearQuantizer - #138

Open
alexey-milovidov wants to merge 1 commit into
szcompressor:masterfrom
ClickHouse:ch-fix-quantizer-nonfinite-cast
Open

Fix undefined behavior casting non-finite / out-of-range float to int in LinearQuantizer#138
alexey-milovidov wants to merge 1 commit into
szcompressor:masterfrom
ClickHouse:ch-fix-quantizer-nonfinite-cast

Conversation

@alexey-milovidov

Copy link
Copy Markdown

Problem

LinearQuantizer::quantize_and_overwrite computes a quantization index by casting a floating-point product to int64_t:

T diff = data - pred;
auto quant_index = static_cast<int64_t>(fabs(diff) * this->error_bound_reciprocal) + 1;

When the input data is NaN, diff is NaN; for infinities or very large magnitudes the product exceeds the int64_t range. Converting such a value to an integer is undefined behavior in C++. It is reported by UndefinedBehaviorSanitizer under float-cast-overflow as ... is outside the range of representable values of type 'long', and yields a garbage index on any platform.

Fix

Compute the scaled magnitude as a double and check that it is within the representable quantization range before the cast. NaN fails the scaled < radius * 2 comparison (any comparison with NaN is false) and infinities/overflows fail it too, so every non-representable value falls through to be stored losslessly in unpred — exactly the same outcome the existing out-of-range branch already produced for finite values. Quantization of representable values is unchanged.

Context

Found while integrating SZ3 into ClickHouse (UBSan build, compressing float columns that contain NaN/Inf). Companion fix on the ClickHouse fork: ClickHouse#3

…verflow

LinearQuantizer::quantize_and_overwrite cast fabs(diff) * error_bound_reciprocal
directly to int64_t. When the input value is NaN, diff is NaN; for infinities or
huge magnitudes the product exceeds the int64_t range. Converting such a value to
an integer is undefined behaviour, reported by UBSan as
'... is outside the range of representable values of type long'.

Check the finite quantization range on the double before the cast. Values that
cannot be represented as an index (NaN, +/-Inf, overflow) are stored losslessly in
unpred, which is the same outcome the out-of-range branch already produced for
finite values, so quantization of representable values is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants