Extend float-to-string `itoa` fast path for large integer values by Algunenano · Pull Request #100649 · ClickHouse/ClickHouse · GitHub
Skip to content

Extend float-to-string itoa fast path for large integer values#100649

Merged
Algunenano merged 10 commits into
ClickHouse:masterfrom
Algunenano:fix-isIntegerInRepresentableRange-float32
Apr 1, 2026
Merged

Extend float-to-string itoa fast path for large integer values#100649
Algunenano merged 10 commits into
ClickHouse:masterfrom
Algunenano:fix-isIntegerInRepresentableRange-float32

Conversation

@Algunenano

@Algunenano Algunenano commented Mar 24, 2026

Copy link
Copy Markdown
Member

Extend the itoa fast path in writeFloatTextFastPath for floating-point values that are integers but whose exponent exceeds the mantissa width (ULP > 1). In this range, multiple integers map to the same float, and shortest-representation algorithms like dragonbox prefer "rounder" decimals (more trailing zeros).

What changed:

  1. For Float32 exp 24 (ULP=2) and Float64 exp 53 (ULP=2): all values are even integers, plain itoa matches dragonbox — added directly to the fast path.

  2. For Float32 exp 25-30 (ULP 4..128): a rounding step finds the "roundest" integer within the valid dragonbox range by trying % 1000, % 100, % 10 from largest to smallest.

  3. For Float64 exp 54-62 (ULP 4..1024): same rounding approach, with an additional % 10000 step for exp 62 (ULP=1024).

  4. All modulo operations use compile-time constants (no hardware div). The IEEE 754 boundary conditions (power-of-2 asymmetry, even/odd mantissa tie-breaking) are handled correctly.

  5. Negative values are handled by writing the sign first, then rounding the absolute value.

  6. Added a performance test for Float64 large integers starting at 2^53.

Range covered:

Type Exponent range Value range ULP range
Float32 exp 0..24 integers up to ~16M 1..2 (plain itoa)
Float32 exp 25..30 ~33M to ~2.1B 4..128 (rounded itoa)
Float64 exp 0..53 integers up to ~9e15 1..2 (plain itoa)
Float64 exp 54..62 ~1.8e16 to ~9.2e18 4..1024 (rounded itoa)

Verification:

  • Float32: exhaustive check of all ~4.3 billion values (positive and negative) — zero mismatches with dragonbox.
  • Float64: 10 billion random samples (500M per exponent, exp 53-62, both positive and negative) plus all power-of-2 boundary values — zero mismatches with dragonbox.

Changelog category (leave one):

  • Performance Improvement

Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):

Faster float-to-string conversion for large integer values by extending the itoa fast path with dragonbox-compatible rounding.

Documentation entry for user-facing changes

  • Documentation is written (mandatory for new features)

Version info

  • Merged into: 26.4.1.487

The previous implementation of `DecomposedFloat::isIntegerInRepresentableRange`
only recognized integers with normalizedExponent <= mantissa_bits (23 for
Float32, 52 for Float64). This missed all integers with higher exponents,
where all mantissa bits contribute to the integer part and no fractional
bits exist.

For Float32, this meant values like 1e9f (exponent 29) were not recognized
as integers, falling through to the slower float-to-string path instead of
the fast `itoa` path in `writeFloatTextFastPath`. This should show as a
performance improvement in the `float_formatting` performance test for Float32.

The fix adds the missing case: when exp > mantissa_bits, the value is always
an integer — we only need to check it fits in the target signed integer type
(Int32 for Float32, Int64 for Float64) via the exp < bits-1 bound.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@clickhouse-gh

clickhouse-gh Bot commented Mar 24, 2026

Copy link
Copy Markdown
Contributor

Workflow [PR], commit [75d4429]

Summary:

job_name test_name status info comment
Integration tests (amd_llvm_coverage, 2/5) failure
test_overcommit_tracker/test.py::test_user_overcommit FAIL cidb IGNORED
Stress test (amd_asan_ubsan) failure
Server died FAIL cidb IGNORED
Logical error: Shard number is greater than shard count: shard_num=A shard_count=B cluster=C (STID: 5066-457d) FAIL cidb, issue ISSUE EXISTS
Stress test (arm_release) failure
Server died FAIL cidb IGNORED
Logical error: Can't adjust last granule because it has A rows, but try to subtract B rows (num_read_rows = C, total_rows_per_granule = D, rows_per_granule = [E], debug: max_rows=F, rows_from_read=G, rows_from_finalize_loop=H, rows_from_finalize_post=I, ranges_processed=J, skipped_marks=K, use_query_condition_cache=L, can_read_incomplete_granules=M) (STID: 4089-428f) FAIL cidb IGNORED
Stress test (arm_msan) failure
Server died FAIL cidb IGNORED
MemorySanitizer: use-of-uninitialized-value (STID: 1003-326e) FAIL cidb IGNORED
Unit tests (asan_ubsan) error IGNORED

AI Review

Summary

This PR extends the float-to-string integer fast path for Float32 and Float64 into high-exponent ranges by adding bounded rounding that matches dragonbox shortest-representation choices, plus coverage tests for those ranges. I reviewed the modified conversion logic and added tests, including boundary behavior (mantissa == 0 asymmetry), signed handling, and tie behavior encoded via adjusted half-ULP bounds. I did not find correctness, safety, concurrency, or performance-blocking issues in the current PR head.

ClickHouse Rules
Item Status Notes
Deletion logging
Serialization versioning
Core-area scrutiny
No test removal
Experimental gate
No magic constants
Backward compatibility
SettingsChangesHistory.cpp
PR metadata quality
Safe rollout
Compilation time
Final Verdict
  • Status: ✅ Approve

@clickhouse-gh clickhouse-gh Bot added the pr-performance Pull request with some performance improvements label Mar 24, 2026
@Algunenano

Copy link
Copy Markdown
Member Author

Failures are a curious: 2^55 was output as 36028797018963970 and now 36028797018963968. Both are the same Float64 value. itoa presents the exact integer, while dragonbox presents a different (but equivalent) shortest representation.

  ┌────────────────────────────┬───────────────────────┐
  │           System           │        Output         │
  ├────────────────────────────┼───────────────────────┤
  │ Python repr                │ 3.602879701896397e+16 │
  ├────────────────────────────┼───────────────────────┤
  │ JavaScript                 │ 36028797018963970     │
  ├────────────────────────────┼───────────────────────┤
  │ C printf %.17g             │ 36028797018963968     │
  ├────────────────────────────┼───────────────────────┤
  │ Dragonbox (old ClickHouse) │ 36028797018963970     │
  ├────────────────────────────┼───────────────────────┤
  │ itoa (new ClickHouse)      │ 36028797018963968     │
  └────────────────────────────┴───────────────────────┘

@Algunenano Algunenano marked this pull request as draft March 24, 2026 18:17
@Algunenano Algunenano self-assigned this Mar 24, 2026
Algunenano and others added 4 commits March 24, 2026 23:03
For Float32 values with normalizedExponent > 24 (roughly 33M to 2B), the
ULP exceeds 1, so multiple integers map to the same float. A plain
Int32 cast gives the exact value, but shortest-representation algorithms
like dragonbox prefer "rounder" decimals (more trailing zeros).

This adds a rounding step that finds the roundest integer within the valid
dragonbox range, matching dragonbox output exactly for all 2.1 billion
positive Float32 values (verified exhaustively).

The rounding uses hardcoded % 10 / % 100 / % 1000 with compile-time
constant divisors (imul+shift, no hardware div). IEEE 754 boundary
tie-breaking (inclusive for even mantissa, exclusive for odd) is folded
into the half_ulp values, eliminating branches.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix stale comments after multiple iterations: separate the general
explanation from the function documentation, correct function name
references, and clarify the exhaustive verification claim.

Simplify the itoa condition in `writeFloatTextFastPath` to use
`isIntegerInRepresentableRange` instead of manual mantissa bit masking.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Same approach as the Float32 rounding: for Float64 values with
normalizedExponent 54-62 (roughly 1.8e16 to 9.2e18), find the "roundest"
integer within the valid dragonbox range using compile-time constant
modulo operations (% 10, % 100, % 1000, % 10000).

Also adds exp=53 (ULP=2) to the plain itoa path, same as Float32 exp=24.

Verified: exhaustive check of all 2.1B positive Float32 values, plus
1 billion random Float64 samples (100M per exponent) and all power-of-2
boundary values — zero mismatches with dragonbox.

Add a performance test for Float64 large integers starting at 2^53.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Algunenano Algunenano changed the title Extend isIntegerInRepresentableRange to cover large float integers Extend float-to-string itoa fast path for large integer values Mar 25, 2026
@Algunenano

Copy link
Copy Markdown
Member Author

Adjusted the new covered range with rounding equivalent to dragonbox so the output is exactly the same, but the performance is several times faster. It should be visible in existing performance test

Comment thread src/IO/WriteHelpers.cpp Outdated
- Fix negative value handling in the rounding path: the modulo logic
  assumed positive values. For negative floats (e.g. BFloat16, negative
  Float64), we now write the sign first, then round the absolute value.
  Fixes 03366_bfloat16_sorting, 03367_bfloat16_tuple_final, and
  01177_group_array_moving test failures.

- Update verification comments to reflect actual testing coverage:
  Float32: all ~4.3B values exhaustively (positive and negative).
  Float64: 10B random samples (500M per exponent × 10 × pos/neg)
  plus all power-of-2 boundaries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Algunenano

Copy link
Copy Markdown
Member Author

The random sampling of queries makes it harder to analyze, but it seems some cases have regressed while others where noticeable (4x!) improved:

● Right — the perf framework randomly samples queries from each test file, so not all 32 queries run in every check. The data I have is the
   complete set of float_formatting queries that were actually run. Here's the full picture:                                               
                                                                                                                                           
  AMD — float_formatting (batch 4, 10 queries sampled)                                                                                     
                                                                                                                                           
  ┌─────┬─────────┬─────────┬────────┬──────────┬───────────────────────────────────────────────┐                                          
  │ Q#  │ Old (s) │ New (s) │  Diff  │ Changed? │                     Query                     │
  ├─────┼─────────┼─────────┼────────┼──────────┼───────────────────────────────────────────────┤                                          
  │ 1   │ 0.0456  │ 0.0504  │ +10.5% │ Yes      │ toString(toFloat64(number % 2))               │
  ├─────┼─────────┼─────────┼────────┼──────────┼───────────────────────────────────────────────┤
  │ 3   │ 0.0515  │ 0.0592  │ +14.9% │ Yes      │ toString(toFloat64(number % 100))             │                                          
  ├─────┼─────────┼─────────┼────────┼──────────┼───────────────────────────────────────────────┤                                          
  │ 5   │ 0.0516  │ 0.0590  │ +14.3% │ Yes      │ toString(toFloat64(number % 10000))           │                                          
  ├─────┼─────────┼─────────┼────────┼──────────┼───────────────────────────────────────────────┤                                          
  │ 9   │ 0.1350  │ 0.1359  │ +0.6%  │ No       │ toString(number / 2)                          │
  ├─────┼─────────┼─────────┼────────┼──────────┼───────────────────────────────────────────────┤                                          
  │ 12  │ 0.2541  │ 0.2539  │ -0.1%  │ No       │ toString(number / 16)                         │
  ├─────┼─────────┼─────────┼────────┼──────────┼───────────────────────────────────────────────┤                                          
  │ 14  │ 0.0392  │ 0.0400  │ +2.0%  │ No       │ toString(toFloat32(number % 2))               │
  ├─────┼─────────┼─────────┼────────┼──────────┼───────────────────────────────────────────────┤                                          
  │ 15  │ 0.0422  │ 0.0419  │ -0.8%  │ No       │ toString(toFloat32(number % 10))              │
  ├─────┼─────────┼─────────┼────────┼──────────┼───────────────────────────────────────────────┤                                          
  │ 20  │ 0.1708  │ 0.1765  │ +3.3%  │ No       │ toString(toFloat32(number % 100 + 0.123))     │
  ├─────┼─────────┼─────────┼────────┼──────────┼───────────────────────────────────────────────┤                                          
  │ 21  │ 0.1896  │ 0.1931  │ +1.8%  │ No       │ toString(toFloat32(number % 1000 + 0.123456)) │
  ├─────┼─────────┼─────────┼────────┼──────────┼───────────────────────────────────────────────┤                                          
  │ 23  │ 0.4288  │ 0.4274  │ -0.4%  │ No       │ toString(rand() / 0xFFFFFFFF)                 │
  └─────┴─────────┴─────────┴────────┴──────────┴───────────────────────────────────────────────┘                                          
                               
  ARM — float_formatting (batch 6, 10 queries sampled)                                                                                     
                               
  ┌─────┬─────────┬─────────┬────────┬──────────┬────────────────────────────────────────────────────┐                                     
  │ Q#  │ Old (s) │ New (s) │  Diff  │ Changed? │                       Query                        │
  ├─────┼─────────┼─────────┼────────┼──────────┼────────────────────────────────────────────────────┤                                     
  │ 31  │ 0.3823  │ 0.0931  │ -75.6% │ Yes      │ toString(toFloat64(number)) starting at 2^53 (NEW) │
  ├─────┼─────────┼─────────┼────────┼──────────┼────────────────────────────────────────────────────┤
  │ 2   │ 0.0575  │ 0.0579  │ +0.6%  │ No       │ toString(toFloat64(number % 10))                   │                                     
  ├─────┼─────────┼─────────┼────────┼──────────┼────────────────────────────────────────────────────┤                                     
  │ 5   │ 0.0684  │ 0.0692  │ +1.1%  │ No       │ toString(toFloat64(number % 10000))                │                                     
  ├─────┼─────────┼─────────┼────────┼──────────┼────────────────────────────────────────────────────┤                                     
  │ 6   │ 0.3386  │ 0.3300  │ -2.6%  │ No       │ toString(toFloat64(number % 100 + 0.5))            │
  ├─────┼─────────┼─────────┼────────┼──────────┼────────────────────────────────────────────────────┤                                     
  │ 10  │ 0.2828  │ 0.2861  │ +1.1%  │ No       │ toString(number / 3)                               │
  ├─────┼─────────┼─────────┼────────┼──────────┼────────────────────────────────────────────────────┤                                     
  │ 11  │ 0.3520  │ 0.3559  │ +1.1%  │ No       │ toString(number / 7)                               │
  ├─────┼─────────┼─────────┼────────┼──────────┼────────────────────────────────────────────────────┤                                     
  │ 15  │ 0.0522  │ 0.0490  │ -6.2%  │ No       │ toString(toFloat32(number % 10))                   │
  ├─────┼─────────┼─────────┼────────┼──────────┼────────────────────────────────────────────────────┤                                     
  │ 18  │ 0.0643  │ 0.0605  │ -5.8%  │ No       │ toString(toFloat32(number % 10000))                │
  ├─────┼─────────┼─────────┼────────┼──────────┼────────────────────────────────────────────────────┤                                     
  │ 22  │ 0.3945  │ 0.4013  │ +1.7%  │ No       │ toString(1 / rand())                               │
  ├─────┼─────────┼─────────┼────────┼──────────┼────────────────────────────────────────────────────┤                                     
  │ 24  │ 0.4626  │ 0.4643  │ +0.3%  │ No       │ toString(0xFFFFFFFF / rand())                      │
  └─────┴─────────┴─────────┴────────┴──────────┴────────────────────────────────────────────────────┘                                     
                               
  Key observations                                                                                                                         
                               
  - The AMD regression is specific to Float64 integer paths — queries #1, #3, #5 all use toFloat64(number % N) and show +10-15%. The       
  Float32 equivalents (#14, #15) and non-integer paths (#20, #21, #23) are unaffected.
  - ARM didn't sample queries #1/#3 for Float64 integers, so we can't directly compare. Query #5 ran on ARM but shows only +1.1% (not      
  significant) — so ARM may not have the same regression, or it's within noise.                                                            
  - ARM Float32 integer paths (#15, #18) actually got ~6% faster (not statistically significant but consistent direction), suggesting the
  Float32 branch restructuring may be a slight win on ARM.                                                                                 
  - Query #31 (new) confirms the 4.1x speedup for the target use case (large Float64 integers at exp 53+).
  - The random sampling means we're missing Float64 queries #0 (bare toFloat64(number)) and #2/#4 (number % 10, number % 1000) on AMD,     
  which would help confirm the pattern.                                                                                                    

@Ergus

Ergus commented Mar 25, 2026

Copy link
Copy Markdown
Member

If you want to give a look to even more interesting ways to improve math operations look at fastermath maybe some tricks there could be useful.

Comment thread src/IO/WriteHelpers.cpp Outdated
Comment thread src/IO/WriteHelpers.cpp
Comment thread src/IO/WriteHelpers.cpp Outdated
Comment thread src/IO/WriteHelpers.cpp Outdated
Comment thread src/IO/WriteHelpers.cpp Outdated
Algunenano and others added 3 commits March 31, 2026 13:16
…ed branch

- Replace TRY_ROUND_TO_SHORTEST macro with `tryRoundToShortest` template
  function for better type safety and debuggability.
- Add NO_INLINE to both rounding functions to prevent the compiler from
  inlining them into the hot dispatch path (was causing a 10-15% Float64
  regression from icache pressure — 1679 bytes inlined vs 284 bytes out-of-line).
- Use unsigned types (UInt32/UInt64) for half_ulp, lo, hi since they are
  always positive after sign handling.
- Collapse the two dependent branches in the pick logic into a single
  `prefer_up` expression, reducing instructions by 12-15%.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Algunenano

Copy link
Copy Markdown
Member Author

I've also applied NO_INLINE to the rounded functions to avoid the regressions on smaller integers. Let's see in the CI with a LTO build

Comment thread tests/performance/float_formatting.xml
Comment thread src/IO/WriteHelpers.cpp Outdated
Add a stateless correctness test for the extended itoa fast path covering
Float32 exp 24-30 and Float64 exp 53-62: power-of-2 boundaries, their
neighbors, negative values, and BFloat16. Fix a typo in a comment
referencing `writeFloatTextFastPathRounded` instead of
`writeFloatTextFastPathFloat32Rounded`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@clickhouse-gh

clickhouse-gh Bot commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

LLVM Coverage Report

Metric Baseline Current Δ
Lines 84.00% 84.00% +0.00%
Functions 90.90% 90.90% +0.00%
Branches 76.50% 76.50% +0.00%

Changed lines: 100.00% (85/85) · Uncovered code

Full report · Diff report

@Algunenano

Copy link
Copy Markdown
Member Author

Looks good now:

● Performance Report — PR #100649                                                                                                          
           
  float_formatting — ARM (arm/3)                                                                                                           
   
  2 statistically significant improvements:                                                                                                
                              
  ┌─────┬────────┬─────────┬─────────┬───────────────────────────────┐
  │  #  │ Change │   Old   │   New   │             Query             │
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────┤
  │ 26  │ -20.0% │ 464.6ms │ 371.7ms │ toString(toFloat64(rand64())) │
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────┤
  │ 27  │ -10.5% │ 283.4ms │ 253.8ms │ toString(toFloat32(rand()))   │                                                                     
  └─────┴────────┴─────────┴─────────┴───────────────────────────────┘                                                                     
                                                                                                                                           
  Remaining 8 queries (unchanged):                                                                                                         
                              
  ┌─────┬────────┬─────────┬─────────┬───────────────────────────────────────────┐
  │  #  │ Change │   Old   │   New   │                   Query                   │
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────────────────┤
  │ 16  │ -6.2%  │ 54.3ms  │ 51.0ms  │ toString(toFloat32(number % 100))         │
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────────────────┤
  │ 18  │ -5.9%  │ 64.4ms  │ 60.7ms  │ toString(toFloat32(number % 10000))       │                                                         
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────────────────┤
  │ 17  │ -5.1%  │ 63.2ms  │ 60.0ms  │ toString(toFloat32(number % 1000))        │                                                         
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────────────────┤                                                         
  │ 7   │ +1.8%  │ 343.0ms │ 349.1ms │ toString(toFloat64(number % 100 + 0.123)) │
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────────────────┤                                                         
  │ 6   │ +0.6%  │ 338.5ms │ 340.7ms │ toString(toFloat64(number % 100 + 0.5))   │
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────────────────┤                                                         
  │ 22  │ +0.6%  │ 397.9ms │ 400.6ms │ toString(1 / rand())                      │
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────────────────┤                                                         
  │ 28  │ +0.5%  │ 294.6ms │ 296.3ms │ toString(toFloat32(rand64()))             │
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────────────────┤                                                         
  │ 23  │ -0.4%  │ 480.1ms │ 478.5ms │ toString(rand() / 0xFFFFFFFF)             │
  └─────┴────────┴─────────┴─────────┴───────────────────────────────────────────┘                                                         
   
  float_formatting — AMD (amd/1)                                                                                                           
                              
  No statistically significant changes. All 10 queries within noise:                                                                       
   
  ┌─────┬────────┬─────────┬─────────┬───────────────────────────────────────────────┐                                                     
  │  #  │ Change │   Old   │   New   │                     Query                     │
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────────────────────┤
  │ 16  │ +9.5%  │ 51.1ms  │ 56.0ms  │ toString(toFloat32(number % 100))             │
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────────────────────┤
  │ 1   │ -4.9%  │ 53.1ms  │ 50.5ms  │ toString(toFloat64(number % 2))               │                                                     
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────────────────────┤
  │ 22  │ +3.4%  │ 373.0ms │ 386.0ms │ toString(1 / rand())                          │                                                     
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────────────────────┤                                                     
  │ 13  │ +2.6%  │ 59.6ms  │ 61.1ms  │ toString(toFloat32(number))                   │
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────────────────────┤                                                     
  │ 18  │ +2.5%  │ 52.7ms  │ 54.1ms  │ toString(toFloat32(number % 10000))           │
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────────────────────┤                                                     
  │ 10  │ +2.0%  │ 251.9ms │ 256.9ms │ toString(number / 3)                          │
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────────────────────┤                                                     
  │ 21  │ +1.8%  │ 207.0ms │ 210.7ms │ toString(toFloat32(number % 1000 + 0.123456)) │
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────────────────────┤                                                     
  │ 12  │ +1.4%  │ 276.4ms │ 280.4ms │ toString(number / 16)                         │
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────────────────────┤                                                     
  │ 29  │ +0.6%  │ 496.3ms │ 499.4ms │ toString(reinterpretAsFloat32(...rand()))     │
  ├─────┼────────┼─────────┼─────────┼───────────────────────────────────────────────┤                                                     
  │ 15  │ -0.2%  │ 45.6ms  │ 45.5ms  │ toString(toFloat32(number % 10))              │
  └─────┴────────┴─────────┴─────────┴───────────────────────────────────────────────┘   

@Algunenano Algunenano requested a review from Ergus April 1, 2026 09:16
@Algunenano Algunenano assigned Ergus and unassigned Algunenano Apr 1, 2026
@Algunenano Algunenano marked this pull request as ready for review April 1, 2026 09:16

@Ergus Ergus left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is fine, just commented to say something ;)

Comment thread src/IO/WriteHelpers.cpp
if (shift >= 10 && tryRoundToShortest<10000>(v, lo, hi, buffer, start, result)) return result;
if (shift >= 7 && tryRoundToShortest<1000>(v, lo, hi, buffer, start, result)) return result;
if (shift >= 3 && tryRoundToShortest<100>(v, lo, hi, buffer, start, result)) return result;
if (tryRoundToShortest<10>(v, lo, hi, buffer, start, result)) return result;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good ;)

Comment thread src/IO/WriteHelpers.cpp

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For short type names I am not a fan of auto, makes the code a bit more obscure for a reader... but that's life.

@Algunenano Algunenano added this pull request to the merge queue Apr 1, 2026
Merged via the queue into ClickHouse:master with commit e6493b7 Apr 1, 2026
157 of 163 checks passed
@Algunenano Algunenano deleted the fix-isIntegerInRepresentableRange-float32 branch April 1, 2026 13:15
@robot-clickhouse robot-clickhouse added the pr-synced-to-cloud The PR is synced to the cloud repo label Apr 1, 2026
pull Bot pushed a commit to AnotherGenZ/ClickHouse that referenced this pull request Jun 7, 2026
The test sorts a hand-built `BFloat16` array that contains five values
which all round to the same `BFloat16` representation (`+0.0`, `+0.0`,
`0.0`, `-0.0`, `0.0`). Equal sort keys have an implementation-defined
relative order; under enough query-plan randomization the relative
order of `-0` against the surrounding `0`s changes, and the result no
longer matches the byte-exact `.reference` file. Pinning
`max_threads = 1` makes the order produced by `MergeSortingTransform`
deterministic and matches the committed reference. The test still
exercises sorting of `BFloat16` values across the full input.

Diagnosed by @alexey-milovidov on
ClickHouse#93830 (comment)

CI history: 4 hits across 4 unrelated PRs in 90 days (ClickHouse#93830, ClickHouse#100983,
ClickHouse#100649, ClickHouse#96130), 0 master failures, 78957 OK runs / 1 FAIL in 30 days.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-performance Pull request with some performance improvements pr-synced-to-cloud The PR is synced to the cloud repo

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants