Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package fast_float for openSUSE:Factory checked in at 2026-06-02 19:46:52 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/fast_float (Old) and /work/SRC/openSUSE:Factory/.fast_float.new.1937 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "fast_float" Tue Jun 2 19:46:52 2026 rev:8 rq:1356525 version:8.2.6 Changes: -------- --- /work/SRC/openSUSE:Factory/fast_float/fast_float.changes 2026-05-16 19:23:56.388599867 +0200 +++ /work/SRC/openSUSE:Factory/.fast_float.new.1937/fast_float.changes 2026-06-02 19:47:13.284886581 +0200 @@ -1,0 +2,10 @@ +Tue Jun 2 04:17:12 UTC 2026 - Jan Engelhardt <[email protected]> + +- Update to release 8.2.6 + * Unrolled the integer-part digit scan (straight-line for the + common 1-5 digit case). + * Added a 4-digit SWAR follow-up to loop_parse_if_eight_digits. + * Parallelized the exhaustive float32 sweeps across hardware + threads. + +------------------------------------------------------------------- Old: ---- fast_float-8.2.5.tar.gz New: ---- fast_float-8.2.6.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ fast_float.spec ++++++ --- /var/tmp/diff_new_pack.Vt05CL/_old 2026-06-02 19:47:14.048918226 +0200 +++ /var/tmp/diff_new_pack.Vt05CL/_new 2026-06-02 19:47:14.052918392 +0200 @@ -17,7 +17,7 @@ Name: fast_float -Version: 8.2.5 +Version: 8.2.6 Release: 0 Summary: Re-implementation of std::from_chars for parsing strings into numbers License: Apache-2.0 OR BSL-1.0 OR MIT ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.Vt05CL/_old 2026-06-02 19:47:14.108920712 +0200 +++ /var/tmp/diff_new_pack.Vt05CL/_new 2026-06-02 19:47:14.112920877 +0200 @@ -1,5 +1,5 @@ -mtime: 1778813088 -commit: 3c9f1c465ed439d5dc9e19b94dbecb93ffd7b85c65fde2ccd507fd4949d76351 +mtime: 1780373873 +commit: e339164932ee4eedc02db4a90ed76a617baf4e844b3025f7a06ad5431876cbd4 url: https://src.opensuse.org/jengelh/fast_float revision: master ++++++ build.specials.obscpio ++++++ ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2026-06-02 06:17:53.000000000 +0200 @@ -0,0 +1 @@ +.osc ++++++ fast_float-8.2.5.tar.gz -> fast_float-8.2.6.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fast_float-8.2.5/.github/workflows/pages.yml new/fast_float-8.2.6/.github/workflows/pages.yml --- old/fast_float-8.2.5/.github/workflows/pages.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/fast_float-8.2.6/.github/workflows/pages.yml 2026-06-02 00:07:41.000000000 +0200 @@ -0,0 +1,64 @@ +name: Deploy GitHub Pages + +on: + push: + branches: [main] + paths: + - "docs/**" + - ".github/workflows/pages.yml" + - "CMakeLists.txt" + release: + types: [published] + workflow_dispatch: + +permissions: + contents: write # Changed: need write to push to gh-pages + pages: read # No longer needed for deploy-pages + id-token: none + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + deploy: + name: Deploy to gh-pages + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Resolve latest release version + id: version + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + tag="$(gh release view --repo ${{ github.repository }} --json tagName --jq .tagName 2>/dev/null || true)" + if [ -z "${tag:-}" ]; then + tag="$(git tag --sort=-v:refname | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1 || true)" + fi + if [ -z "${tag:-}" ]; then + tag="v$(grep -E 'project\(fast_float VERSION' CMakeLists.txt | sed -E 's/.*VERSION ([0-9.]+).*/\1/')" + fi + version="${tag#v}" + echo "tag=${tag}" >> "$GITHUB_OUTPUT" + echo "version=${version}" >> "$GITHUB_OUTPUT" + echo "Resolved version: ${version}" + + - name: Substitute version into HTML + run: | + version='${{ steps.version.outputs.version }}' + find docs -type f \( -name '*.html' -o -name '*.md' -o -name '*.css' -o -name '*.js' \) \ + -exec sed -i "s/{{VERSION}}/${version}/g" {} + + + - name: Deploy to gh-pages branch + uses: JamesIves/github-pages-deploy-action@v4 + with: + branch: gh-pages + folder: docs + clean: true + commit-message: "Deploy docs for version ${{ steps.version.outputs.version }}" \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fast_float-8.2.5/CMakeLists.txt new/fast_float-8.2.6/CMakeLists.txt --- old/fast_float-8.2.5/CMakeLists.txt 2026-04-16 20:39:03.000000000 +0200 +++ new/fast_float-8.2.6/CMakeLists.txt 2026-06-02 00:07:41.000000000 +0200 @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.14) -project(fast_float VERSION 8.2.5 LANGUAGES CXX) +project(fast_float VERSION 8.2.6 LANGUAGES CXX) set(FASTFLOAT_CXX_STANDARD 11 CACHE STRING "the C++ standard to use for fastfloat") set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD}) option(FASTFLOAT_TEST "Enable tests" OFF) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fast_float-8.2.5/README.md new/fast_float-8.2.6/README.md --- old/fast_float-8.2.5/README.md 2026-04-16 20:39:03.000000000 +0200 +++ new/fast_float-8.2.6/README.md 2026-06-02 00:07:41.000000000 +0200 @@ -441,7 +441,6 @@ * [WebKit](https://github.com/WebKit/WebKit), the engine behind Safari (Apple's web browser), * [DuckDB](https://duckdb.org), -* [Redis](https://github.com/redis/redis) and [Valkey](https://github.com/valkey-io/valkey), * [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times, * [Google Jsonnet](https://github.com/google/jsonnet), @@ -455,15 +454,6 @@ has a derived implementation that is inherited by the [Ladybird Browser](https://github.com/LadybirdBrowser/ladybird). -The fast_float library provides a performance similar to that of the -[fast_double_parser](https://github.com/lemire/fast_double_parser) library but -using an updated algorithm reworked from the ground up, and while offering an -API more in line with the expectations of C++ programmers. The -fast_double_parser library is part of the [Microsoft LightGBM machine-learning -framework](https://github.com/microsoft/LightGBM). - - - Packages ------ @@ -541,7 +531,7 @@ FetchContent_Declare( fast_float GIT_REPOSITORY https://github.com/fastfloat/fast_float.git - GIT_TAG tags/v8.2.5 + GIT_TAG tags/v8.2.6 GIT_SHALLOW TRUE) FetchContent_MakeAvailable(fast_float) @@ -557,7 +547,7 @@ CPMAddPackage( NAME fast_float GITHUB_REPOSITORY "fastfloat/fast_float" - GIT_TAG v8.2.5) + GIT_TAG v8.2.6) ``` ## Using as single header @@ -569,7 +559,7 @@ You may directly download automatically generated single-header files: -<https://github.com/fastfloat/fast_float/releases/download/v8.2.5/fast_float.h> +<https://github.com/fastfloat/fast_float/releases/download/v8.2.6/fast_float.h> ## Benchmarking diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fast_float-8.2.5/docs/README.md new/fast_float-8.2.6/docs/README.md --- old/fast_float-8.2.5/docs/README.md 1970-01-01 01:00:00.000000000 +0100 +++ new/fast_float-8.2.6/docs/README.md 2026-06-02 00:07:41.000000000 +0200 @@ -0,0 +1,25 @@ +# fast_float website + +The source for <https://fastfloat.github.io/fast_float/>. + +Static HTML, CSS, and a small JS file — no build step required. To preview +locally, serve this directory with any static file server, for example: + +```bash +python3 -m http.server -d docs 8080 +# then open http://localhost:8080/ +``` + +## How the version number stays current + +The displayed release version is kept fresh by two independent mechanisms: + +1. **Build-time substitution.** The `Deploy GitHub Pages` workflow + (`.github/workflows/pages.yml`) replaces every occurrence of the literal + `{{VERSION}}` in `index.html` with the latest GitHub release tag before + publishing. The workflow runs on every push to `main` that touches + `docs/**`, on every published release, and can be dispatched manually. + +2. **Client-side refresh.** `assets/app.js` queries the GitHub Releases API + on page load and overwrites any element marked with `data-version`. This + means visitors see the very latest tag even between deploys. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fast_float-8.2.5/docs/assets/app.js new/fast_float-8.2.6/docs/assets/app.js --- old/fast_float-8.2.5/docs/assets/app.js 1970-01-01 01:00:00.000000000 +0100 +++ new/fast_float-8.2.6/docs/assets/app.js 2026-06-02 00:07:41.000000000 +0200 @@ -0,0 +1,116 @@ +(function () { + "use strict"; + + // ---------- Theme toggle ---------- + const root = document.documentElement; + const storedTheme = localStorage.getItem("ff-theme"); + if (storedTheme === "light" || storedTheme === "dark") { + root.setAttribute("data-theme", storedTheme); + } + const toggle = document.getElementById("theme-toggle"); + if (toggle) { + toggle.addEventListener("click", function () { + const current = + root.getAttribute("data-theme") || + (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"); + const next = current === "dark" ? "light" : "dark"; + root.setAttribute("data-theme", next); + localStorage.setItem("ff-theme", next); + }); + } + + // ---------- Copy-to-clipboard on code blocks ---------- + document.querySelectorAll("pre > code").forEach(function (code) { + const pre = code.parentElement; + if (!pre || pre.querySelector(".copy-btn")) return; + const btn = document.createElement("button"); + btn.className = "copy-btn"; + btn.type = "button"; + btn.textContent = "Copy"; + btn.setAttribute("aria-label", "Copy code to clipboard"); + btn.addEventListener("click", function () { + const text = code.innerText; + const done = function () { + btn.textContent = "Copied"; + btn.classList.add("copied"); + setTimeout(function () { + btn.textContent = "Copy"; + btn.classList.remove("copied"); + }, 1400); + }; + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(text).then(done, function () { + fallbackCopy(text); + done(); + }); + } else { + fallbackCopy(text); + done(); + } + }); + pre.appendChild(btn); + }); + + function fallbackCopy(text) { + const ta = document.createElement("textarea"); + ta.value = text; + ta.style.position = "fixed"; + ta.style.opacity = "0"; + document.body.appendChild(ta); + ta.select(); + try { document.execCommand("copy"); } catch (e) { /* ignore */ } + document.body.removeChild(ta); + } + + // ---------- Live version refresh from GitHub Releases ---------- + // The build-time workflow substitutes {{VERSION}} in the HTML; this + // additionally refreshes the displayed version in the browser so the + // site always reflects the very latest release without redeploying. + const versionNodes = document.querySelectorAll("[data-version]"); + const downloadLink = document.getElementById("download-latest"); + if (versionNodes.length === 0) return; + + // Don't refetch more than once per hour per visitor. + const CACHE_KEY = "ff-latest-release"; + const CACHE_TTL = 60 * 60 * 1000; + let cached = null; + try { + const raw = localStorage.getItem(CACHE_KEY); + if (raw) { + const parsed = JSON.parse(raw); + if (parsed && parsed.ts && Date.now() - parsed.ts < CACHE_TTL) { + cached = parsed; + } + } + } catch (e) { /* ignore */ } + + if (cached && cached.tag) { + applyVersion(cached.tag, cached.url); + } else { + fetch("https://api.github.com/repos/fastfloat/fast_float/releases/latest", { + headers: { Accept: "application/vnd.github+json" }, + }) + .then(function (r) { return r.ok ? r.json() : null; }) + .then(function (data) { + if (!data || !data.tag_name) return; + try { + localStorage.setItem( + CACHE_KEY, + JSON.stringify({ ts: Date.now(), tag: data.tag_name, url: data.html_url }) + ); + } catch (e) { /* ignore */ } + applyVersion(data.tag_name, data.html_url); + }) + .catch(function () { /* offline / rate limited — keep build-time value */ }); + } + + function applyVersion(tag, url) { + const clean = tag.replace(/^v/, ""); + versionNodes.forEach(function (el) { + // Preserve a leading "v" if the original text used one. + const wasV = (el.textContent || "").trim().startsWith("v"); + el.textContent = (wasV ? "v" : "") + clean; + }); + if (downloadLink && url) downloadLink.href = url; + } +})(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fast_float-8.2.5/docs/assets/logo.svg new/fast_float-8.2.6/docs/assets/logo.svg --- old/fast_float-8.2.5/docs/assets/logo.svg 1970-01-01 01:00:00.000000000 +0100 +++ new/fast_float-8.2.6/docs/assets/logo.svg 2026-06-02 00:07:41.000000000 +0200 @@ -0,0 +1,15 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64"> + <defs> + <linearGradient id="g" x1="0" y1="0" x2="1" y2="1"> + <stop offset="0%" stop-color="#2563eb"/> + <stop offset="100%" stop-color="#0ea5e9"/> + </linearGradient> + </defs> + <rect x="2" y="2" width="60" height="60" rx="14" fill="url(#g)"/> + <path d="M16 44 L28 20 L36 36 L44 28 L52 44" + fill="none" stroke="#ffffff" stroke-width="4" + stroke-linecap="round" stroke-linejoin="round"/> + <circle cx="28" cy="20" r="2.5" fill="#ffffff"/> + <circle cx="36" cy="36" r="2.5" fill="#ffffff"/> + <circle cx="44" cy="28" r="2.5" fill="#ffffff"/> +</svg> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fast_float-8.2.5/docs/assets/style.css new/fast_float-8.2.6/docs/assets/style.css --- old/fast_float-8.2.5/docs/assets/style.css 1970-01-01 01:00:00.000000000 +0100 +++ new/fast_float-8.2.6/docs/assets/style.css 2026-06-02 00:07:41.000000000 +0200 @@ -0,0 +1,454 @@ +:root { + --bg: #ffffff; + --bg-alt: #f7f8fb; + --surface: #ffffff; + --text: #0f172a; + --text-muted: #475569; + --border: #e2e8f0; + --accent: #2563eb; + --accent-strong: #1d4ed8; + --accent-soft: rgba(37, 99, 235, 0.10); + --grad-from: #2563eb; + --grad-to: #0ea5e9; + --code-bg: #0f172a; + --code-text: #e2e8f0; + --shadow: 0 1px 2px rgba(15, 23, 42, 0.04), 0 8px 24px rgba(15, 23, 42, 0.06); + --radius: 12px; + --radius-sm: 8px; + --maxw: 1100px; +} + +:root[data-theme="dark"] { + --bg: #0b1020; + --bg-alt: #0f172a; + --surface: #121a2e; + --text: #e6edf7; + --text-muted: #94a3b8; + --border: #1f2a44; + --accent: #60a5fa; + --accent-strong: #93c5fd; + --accent-soft: rgba(96, 165, 250, 0.14); + --grad-from: #60a5fa; + --grad-to: #22d3ee; + --code-bg: #060a17; + --code-text: #e6edf7; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px rgba(0, 0, 0, 0.35); +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) { + --bg: #0b1020; + --bg-alt: #0f172a; + --surface: #121a2e; + --text: #e6edf7; + --text-muted: #94a3b8; + --border: #1f2a44; + --accent: #60a5fa; + --accent-strong: #93c5fd; + --accent-soft: rgba(96, 165, 250, 0.14); + --grad-from: #60a5fa; + --grad-to: #22d3ee; + --code-bg: #060a17; + --code-text: #e6edf7; + --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px rgba(0, 0, 0, 0.35); + } +} + +* { box-sizing: border-box; } + +html { scroll-behavior: smooth; } + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; + font-size: 16px; + line-height: 1.6; + color: var(--text); + background: var(--bg); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { color: var(--accent); text-decoration: none; } +a:hover { text-decoration: underline; } + +code, pre { + font-family: "SF Mono", Menlo, Consolas, "Liberation Mono", "Roboto Mono", monospace; + font-size: 0.92em; +} +:not(pre) > code { + background: var(--accent-soft); + color: var(--accent-strong); + padding: 0.12em 0.4em; + border-radius: 4px; + font-size: 0.88em; +} + +pre { + background: var(--code-bg); + color: var(--code-text); + padding: 1.1rem 1.2rem; + border-radius: var(--radius); + overflow-x: auto; + margin: 1rem 0 1.5rem; + position: relative; + box-shadow: var(--shadow); + font-size: 0.88rem; + line-height: 1.55; +} +pre code { background: transparent; color: inherit; padding: 0; } +/* highlight.js applies its own background — keep our pre styling */ +pre code.hljs { background: transparent; padding: 0; } + +.copy-btn { + position: absolute; + top: 8px; + right: 8px; + background: rgba(255, 255, 255, 0.08); + color: var(--code-text); + border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: 6px; + padding: 4px 10px; + font-size: 0.75rem; + cursor: pointer; + opacity: 0; + transition: opacity 0.15s ease, background 0.15s ease; +} +pre:hover .copy-btn { opacity: 1; } +.copy-btn:hover { background: rgba(255, 255, 255, 0.15); } +.copy-btn.copied { background: rgba(34, 197, 94, 0.25); border-color: rgba(34, 197, 94, 0.5); } + +.container { + max-width: var(--maxw); + margin: 0 auto; + padding: 0 1.25rem; +} + +.skip { + position: absolute; left: -9999px; + background: var(--accent); color: white; + padding: 0.6rem 1rem; border-radius: 0 0 6px 0; +} +.skip:focus { left: 0; top: 0; } + +/* Header */ +.site-header { + position: sticky; + top: 0; + z-index: 50; + backdrop-filter: saturate(180%) blur(12px); + -webkit-backdrop-filter: saturate(180%) blur(12px); + background: color-mix(in srgb, var(--bg) 80%, transparent); + border-bottom: 1px solid var(--border); +} +.nav { + display: flex; + align-items: center; + gap: 1.25rem; + height: 60px; +} +.brand { + display: inline-flex; + align-items: center; + gap: 0.55rem; + font-weight: 700; + color: var(--text); + text-decoration: none; +} +.brand img { display: block; } +.primary-nav { + display: flex; + gap: 1.2rem; + margin-left: 1rem; + flex: 1; +} +.primary-nav a { + color: var(--text-muted); + font-size: 0.92rem; + font-weight: 500; +} +.primary-nav a:hover { color: var(--text); text-decoration: none; } +.nav-actions { display: flex; align-items: center; gap: 0.5rem; } + +.ghost-btn { + display: inline-flex; align-items: center; gap: 0.4rem; + padding: 0.45rem 0.8rem; + border: 1px solid var(--border); + border-radius: 8px; + color: var(--text); + font-size: 0.9rem; + background: var(--surface); + transition: border-color 0.15s, background 0.15s; +} +.ghost-btn:hover { border-color: var(--accent); text-decoration: none; } + +.theme-toggle { + width: 36px; height: 36px; + display: inline-flex; align-items: center; justify-content: center; + border: 1px solid var(--border); + border-radius: 8px; + background: var(--surface); + color: var(--text); + cursor: pointer; +} +.theme-toggle .i-moon { display: none; } +.theme-toggle .i-sun { display: block; } +:root[data-theme="dark"] .theme-toggle .i-sun, +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) .theme-toggle .i-sun { display: none; } + :root:not([data-theme="light"]) .theme-toggle .i-moon { display: block; } +} +:root[data-theme="dark"] .theme-toggle .i-sun { display: none; } +:root[data-theme="dark"] .theme-toggle .i-moon { display: block; } +:root[data-theme="light"] .theme-toggle .i-sun { display: block; } +:root[data-theme="light"] .theme-toggle .i-moon { display: none; } + +/* Hero */ +.hero { + padding: 5rem 0 4rem; + background: + radial-gradient(1200px 480px at 50% -10%, var(--accent-soft), transparent 70%), + var(--bg); + border-bottom: 1px solid var(--border); +} +.eyebrow { + display: inline-block; + font-size: 0.78rem; + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--accent); + background: var(--accent-soft); + padding: 0.3rem 0.7rem; + border-radius: 999px; + margin-bottom: 1.25rem; +} +.hero h1 { + font-size: clamp(2rem, 4.6vw, 3.6rem); + line-height: 1.1; + margin: 0 0 1.25rem; + font-weight: 800; + letter-spacing: -0.02em; +} +.grad { + background: linear-gradient(90deg, var(--grad-from), var(--grad-to)); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} +.lede { + font-size: 1.15rem; + color: var(--text-muted); + max-width: 720px; + margin: 0 0 2rem; +} +.lede strong { color: var(--text); } + +.cta-row { display: flex; flex-wrap: wrap; gap: 0.7rem; margin-bottom: 1.75rem; } + +.btn { + display: inline-flex; align-items: center; gap: 0.5rem; + padding: 0.7rem 1.1rem; + border-radius: 10px; + font-weight: 600; + font-size: 0.95rem; + border: 1px solid var(--border); + background: var(--surface); + color: var(--text); + cursor: pointer; + transition: transform 0.05s ease, border-color 0.15s, background 0.15s; +} +.btn:hover { text-decoration: none; border-color: var(--accent); } +.btn:active { transform: translateY(1px); } +.btn.primary { + background: var(--accent); + border-color: var(--accent); + color: white; + box-shadow: 0 6px 20px rgba(37, 99, 235, 0.25); +} +.btn.primary:hover { background: var(--accent-strong); border-color: var(--accent-strong); } +.btn.ghost { background: transparent; } +.btn code { background: rgba(255, 255, 255, 0.18); color: inherit; padding: 0.1em 0.45em; border-radius: 4px; } +.btn:not(.primary) code { background: var(--accent-soft); color: var(--accent-strong); } + +.hero-meta { + display: flex; + flex-wrap: wrap; + gap: 0.6rem 1rem; + align-items: center; + font-size: 0.9rem; + color: var(--text-muted); +} +.badge { + display: inline-flex; align-items: center; gap: 0.5rem; + padding: 0.3rem 0.7rem; + border: 1px solid var(--border); + border-radius: 999px; + background: var(--surface); +} +.badge code { background: transparent; color: var(--text); padding: 0; } +.badge .dot { + width: 8px; height: 8px; border-radius: 50%; + background: #22c55e; + box-shadow: 0 0 0 4px rgba(34, 197, 94, 0.18); +} +.meta-link { color: var(--text-muted); } +.meta-link:hover { color: var(--accent); } + +/* Sections */ +.section { padding: 4.5rem 0; } +.section.alt { background: var(--bg-alt); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); } +.section h2 { + font-size: clamp(1.6rem, 3vw, 2.2rem); + margin: 0 0 0.5rem; + letter-spacing: -0.01em; +} +.section h3 { margin: 1.75rem 0 0.6rem; font-size: 1.1rem; } +.section-lede { color: var(--text-muted); max-width: 720px; margin: 0 0 2rem; } + +/* Grid cards */ +.grid { display: grid; gap: 1rem; } +.grid.features { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); } +.grid.install { grid-template-columns: 1fr; } + +.card { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 1.4rem 1.4rem 1.5rem; + box-shadow: var(--shadow); + transition: transform 0.12s ease, border-color 0.15s; +} +.card:hover { transform: translateY(-2px); border-color: var(--accent); } +.card h3 { margin: 0 0 0.5rem; font-size: 1.05rem; } +.card p, .card ul { margin: 0; color: var(--text-muted); font-size: 0.95rem; } +.card pre { margin: 0.6rem 0 0; font-size: 0.82rem; } + +.bullets { padding-left: 1.1rem; } +.bullets li { margin: 0.2rem 0; } + +/* Benchmarks */ +.bench { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 1.5rem; + box-shadow: var(--shadow); +} +.bench-row { + display: grid; + grid-template-columns: 160px 1fr; + align-items: center; + gap: 0.9rem; + margin: 0.55rem 0; +} +.bench-label { font-weight: 600; color: var(--text); font-size: 0.95rem; } +.bench-bar { + background: var(--bg-alt); + border-radius: 6px; + overflow: hidden; + height: 28px; + position: relative; +} +.bench-fill { + display: inline-flex; + align-items: center; + height: 100%; + width: var(--w, 0%); + background: linear-gradient(90deg, color-mix(in srgb, var(--accent) 35%, transparent), color-mix(in srgb, var(--accent) 55%, transparent)); + color: var(--text); + padding: 0 0.7rem; + border-radius: 6px; + font-variant-numeric: tabular-nums; + font-size: 0.85rem; + font-weight: 600; + white-space: nowrap; +} +.bench-fill.primary { + background: linear-gradient(90deg, var(--grad-from), var(--grad-to)); + color: white; +} + +@media (max-width: 600px) { + .bench-row { grid-template-columns: 1fr; gap: 0.3rem; } +} + +.repro { + margin-top: 1.25rem; + border: 1px solid var(--border); + border-radius: var(--radius-sm); + background: var(--surface); + padding: 0.4rem 1rem; +} +.repro summary { + cursor: pointer; + padding: 0.5rem 0; + font-weight: 600; + color: var(--text-muted); +} +.repro[open] summary { color: var(--text); } +.repro pre { margin: 0.5rem 0 0.75rem; } + +/* Users list */ +.users { + list-style: none; + padding: 0; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); + gap: 0.6rem 1rem; +} +.users li { + padding: 0.7rem 0.9rem; + border: 1px solid var(--border); + border-radius: var(--radius-sm); + background: var(--surface); + font-size: 0.92rem; + color: var(--text-muted); +} +.users li strong { color: var(--text); margin-right: 0.35rem; } +.aside { color: var(--text-muted); margin-top: 1.5rem; font-size: 0.92rem; } + +.papers { list-style: none; padding: 0; } +.papers li { + padding: 1rem 1.2rem; + border: 1px solid var(--border); + border-radius: var(--radius-sm); + background: var(--surface); + margin-bottom: 0.7rem; +} + +/* Footer */ +.site-footer { + background: var(--bg-alt); + border-top: 1px solid var(--border); + padding: 3rem 0 2rem; + color: var(--text-muted); + font-size: 0.92rem; + margin-top: 2rem; +} +.footer-grid { + display: grid; + grid-template-columns: 1.5fr 1fr 1fr; + gap: 2rem; + margin-bottom: 2rem; +} +.site-footer h4 { color: var(--text); font-size: 0.95rem; margin: 0 0 0.6rem; } +.site-footer ul { list-style: none; padding: 0; margin: 0; } +.site-footer li { margin: 0.25rem 0; } +.subfoot { + display: flex; + justify-content: space-between; + align-items: center; + padding-top: 1.5rem; + border-top: 1px solid var(--border); + flex-wrap: wrap; + gap: 0.5rem; +} +.subfoot code { background: var(--surface); color: var(--text); border: 1px solid var(--border); } + +@media (max-width: 720px) { + .primary-nav { display: none; } + .footer-grid { grid-template-columns: 1fr; gap: 1.5rem; } + .hero { padding: 3rem 0 2.5rem; } + .section { padding: 3rem 0; } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fast_float-8.2.5/docs/index.html new/fast_float-8.2.6/docs/index.html --- old/fast_float-8.2.5/docs/index.html 1970-01-01 01:00:00.000000000 +0100 +++ new/fast_float-8.2.6/docs/index.html 2026-06-02 00:07:41.000000000 +0200 @@ -0,0 +1,349 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>fast_float — parse floating-point numbers at a gigabyte per second</title> + <meta name="description" content="A header-only C++ library for fast and exact parsing of floating-point and integer numbers. Used by GCC, Chromium, WebKit, LLVM, Apache Arrow, DuckDB, Redis, and more." /> + <meta name="theme-color" content="#0f172a" /> + + <meta property="og:title" content="fast_float — parse floats at 1 GB/s" /> + <meta property="og:description" content="A header-only C++ library for fast and exact floating-point and integer parsing." /> + <meta property="og:type" content="website" /> + <meta property="og:url" content="https://fastfloat.github.io/fast_float/" /> + <meta name="twitter:card" content="summary_large_image" /> + + <link rel="icon" type="image/svg+xml" href="assets/logo.svg" /> + <link rel="stylesheet" href="assets/style.css" /> + <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/[email protected]/build/styles/github-dark.min.css" /> +</head> +<body> + <a class="skip" href="#main">Skip to content</a> + + <header class="site-header"> + <div class="container nav"> + <a class="brand" href="./"> + <img src="assets/logo.svg" alt="" width="32" height="32" /> + <span>fast_float</span> + </a> + <nav class="primary-nav" aria-label="Primary"> + <a href="#features">Features</a> + <a href="#performance">Performance</a> + <a href="#quickstart">Quick start</a> + <a href="#users">Users</a> + <a href="#install">Install</a> + </nav> + <div class="nav-actions"> + <a class="ghost-btn" href="https://github.com/fastfloat/fast_float" aria-label="GitHub repository"> + <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.65.5.5 5.65.5 12c0 5.08 3.29 9.39 7.86 10.91.58.1.79-.25.79-.56v-2c-3.2.7-3.88-1.37-3.88-1.37-.52-1.33-1.27-1.69-1.27-1.69-1.04-.71.08-.7.08-.7 1.15.08 1.76 1.18 1.76 1.18 1.03 1.76 2.7 1.25 3.36.96.1-.74.4-1.25.73-1.54-2.55-.29-5.24-1.28-5.24-5.7 0-1.26.45-2.29 1.18-3.1-.12-.29-.51-1.46.11-3.05 0 0 .97-.31 3.18 1.18a11 11 0 0 1 5.78 0c2.2-1.49 3.17-1.18 3.17-1.18.62 1.59.23 2.76.11 3.05.74.81 1.18 1.84 1.18 3.1 0 4.43-2.69 5.4-5.25 5.69.41.36.78 1.07.78 2.16v3.2c0 .31.21.67.8.56A11.5 11.5 0 0 0 23.5 12C23.5 5.65 18.35.5 12 .5z"/></svg> + <span>GitHub</span> + </a> + <button class="theme-toggle" id="theme-toggle" aria-label="Toggle color theme" title="Toggle theme"> + <svg class="i-sun" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg> + <svg class="i-moon" width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg> + </button> + </div> + </div> + </header> + + <main id="main"> + <section class="hero"> + <div class="container"> + <span class="eyebrow">C++11 · header-only · triple-licensed</span> + <h1>Parse floating-point numbers <span class="grad">at a gigabyte per second</span>.</h1> + <p class="lede"> + <strong>fast_float</strong> is a header-only C++ implementation of + <code>std::from_chars</code> for <code>float</code>, <code>double</code>, and integer types. + Exact IEEE rounding, no allocations, no exceptions — often <strong>many times faster</strong> + than your standard library. + </p> + <div class="cta-row"> + <a class="btn primary" href="#quickstart">Get started</a> + <a class="btn" href="https://github.com/fastfloat/fast_float">View on GitHub</a> + <a class="btn ghost" href="https://github.com/fastfloat/fast_float/releases/latest" id="download-latest"> + Download <code class="version-tag" data-version>v{{VERSION}}</code> + </a> + </div> + <div class="hero-meta"> + <span class="badge" id="release-badge"> + <span class="dot"></span> Latest release: <code data-version>v{{VERSION}}</code> + </span> + <a class="meta-link" href="https://github.com/fastfloat/fast_float/blob/main/LICENSE-APACHE">Apache 2.0</a> + <a class="meta-link" href="https://github.com/fastfloat/fast_float/blob/main/LICENSE-MIT">MIT</a> + <a class="meta-link" href="https://github.com/fastfloat/fast_float/blob/main/LICENSE-BOOST">Boost</a> + </div> + </div> + </section> + + <section id="features" class="section"> + <div class="container"> + <h2>Why fast_float?</h2> + <p class="section-lede">A drop-in <code>from_chars</code> built for performance-critical code paths.</p> + <div class="grid features"> + <article class="card"> + <h3>Blazing fast</h3> + <p>Often <strong>4× faster</strong> than the best competitor and many times faster than typical standard-library implementations. Sustains <strong>1 GB/s</strong> on commodity hardware.</p> + </article> + <article class="card"> + <h3>Exact rounding</h3> + <p>Returns the closest IEEE 754 <code>float</code> or <code>double</code> with round-to-nearest, ties-to-even — bit-for-bit correct.</p> + </article> + <article class="card"> + <h3>Header-only</h3> + <p>Just drop in <code>fast_float.h</code> or use it via CMake, Conan, vcpkg, xmake, or Homebrew. Requires only C++11.</p> + </article> + <article class="card"> + <h3>No surprises</h3> + <p>Does not allocate, does not throw, locale-independent. The interface mirrors C++17 <code>std::from_chars</code>.</p> + </article> + <article class="card"> + <h3>Integers too</h3> + <p>Parses every standard integer type in bases 2–36, plus <code>bool</code>. The same fast, allocation-free interface.</p> + </article> + <article class="card"> + <h3>Unicode & formats</h3> + <p>UTF-8, UTF-16, and UTF-32 inputs. JSON, Fortran, and custom decimal separators via <code>from_chars_advanced</code>.</p> + </article> + <article class="card"> + <h3>constexpr-ready</h3> + <p>In C++20, parse strings at compile time with <code>consteval</code> — zero runtime cost.</p> + </article> + <article class="card"> + <h3>Portable</h3> + <p>Visual Studio, GCC, Clang, MSYS2. Linux, macOS, FreeBSD, Windows. x86-64, ARM, RISC-V, s390x. 32-bit and 64-bit.</p> + </article> + </div> + </div> + </section> + + <section id="performance" class="section alt"> + <div class="container"> + <h2>Performance</h2> + <p class="section-lede"> + Parsing random floating-point numbers, measured in megabytes per second + (higher is better). Source: project benchmark suite on a typical x86-64 box. + </p> + + <div class="bench"> + <div class="bench-row"> + <span class="bench-label">fast_float</span> + <div class="bench-bar"><span class="bench-fill primary" style="--w: 100%">1042 MB/s</span></div> + </div> + <div class="bench-row"> + <span class="bench-label">abseil</span> + <div class="bench-bar"><span class="bench-fill" style="--w: 41%">430 MB/s</span></div> + </div> + <div class="bench-row"> + <span class="bench-label">netlib</span> + <div class="bench-bar"><span class="bench-fill" style="--w: 26%">271 MB/s</span></div> + </div> + <div class="bench-row"> + <span class="bench-label">double-conversion</span> + <div class="bench-bar"><span class="bench-fill" style="--w: 22%">225 MB/s</span></div> + </div> + <div class="bench-row"> + <span class="bench-label">strtod</span> + <div class="bench-bar"><span class="bench-fill" style="--w: 18%">191 MB/s</span></div> + </div> + </div> + + <details class="repro"> + <summary>Reproduce these numbers</summary> +<pre data-lang="bash"><code>cmake -B build -D FASTFLOAT_BENCHMARKS=ON +cmake --build build +./build/benchmarks/benchmark</code></pre> + </details> + </div> + </section> + + <section id="quickstart" class="section"> + <div class="container"> + <h2>Quick start</h2> + <p class="section-lede">Parse a <code>double</code> from a string in three lines.</p> + +<pre data-lang="cpp"><code>#include "fast_float/fast_float.h" +#include <iostream> +#include <string> + +int main() { + std::string input = "3.1416 xyz "; + double result; + auto answer = fast_float::from_chars(input.data(), + input.data() + input.size(), + result); + if (answer.ec != std::errc()) { + std::cerr << "parsing failure\n"; + return EXIT_FAILURE; + } + std::cout << "parsed the number " << result << '\n'; +}</code></pre> + + <h3>Integers in any base (2–36)</h3> +<pre data-lang="cpp"><code>uint64_t value; +std::string hex = "4f0cedc95a718c"; +auto r = fast_float::from_chars(hex.data(), hex.data() + hex.size(), value, 16); +// value == 22250738585072012</code></pre> + + <h3>UTF-16 input</h3> +<pre data-lang="cpp"><code>std::u16string input = u"3.1416 xyz "; +double result; +auto r = fast_float::from_chars(input.data(), input.data() + input.size(), result);</code></pre> + + <h3>Comma as decimal separator, Fortran, or JSON</h3> +<pre data-lang="cpp"><code>// "3,1416" — French-style +fast_float::parse_options opts{fast_float::chars_format::general, ','}; +fast_float::from_chars_advanced(s.data(), s.data() + s.size(), result, opts); + +// "1d+4" — Fortran exponent +opts = {fast_float::chars_format::fortran}; + +// strict JSON per RFC 8259 +opts = {fast_float::chars_format::json};</code></pre> + + <h3>C++20: parse at compile time</h3> +<pre data-lang="cpp"><code>consteval double parse(std::string_view s) { + double v; + auto r = fast_float::from_chars(s.data(), s.data() + s.size(), v); + return r.ec == std::errc() ? v : -1.0; +} +constexpr double pi = parse("3.1415"); // computed at compile time</code></pre> + </div> + </section> + + <section id="install" class="section alt"> + <div class="container"> + <h2>Install</h2> + <p class="section-lede">Pick the workflow that matches your project.</p> + + <div class="grid install"> + <article class="card"> + <h3>CMake <code>FetchContent</code></h3> +<pre data-lang="cmake"><code>FetchContent_Declare( + fast_float + GIT_REPOSITORY https://github.com/fastfloat/fast_float.git + GIT_TAG tags/v{{VERSION}} + GIT_SHALLOW TRUE) +FetchContent_MakeAvailable(fast_float) +target_link_libraries(myprogram PUBLIC fast_float)</code></pre> + </article> + + <article class="card"> + <h3>CPM</h3> +<pre data-lang="cmake"><code>CPMAddPackage( + NAME fast_float + GITHUB_REPOSITORY "fastfloat/fast_float" + GIT_TAG v{{VERSION}})</code></pre> + </article> + + <article class="card"> + <h3>Single header</h3> + <p>Download a pre-amalgamated header — no build system required.</p> +<pre data-lang="bash"><code>curl -LO https://github.com/fastfloat/fast_float/releases/download/v{{VERSION}}/fast_float.h</code></pre> + </article> + + <article class="card"> + <h3>Package managers</h3> + <ul class="bullets"> + <li><a href="https://conan.io/center/recipes/fast_float">Conan</a></li> + <li><a href="https://formulae.brew.sh/formula/fast_float">Homebrew</a> — <code>brew install fast_float</code></li> + <li><a href="https://xmake.io">xmake</a></li> + <li>Fedora — <code>dnf install fast_float-devel</code></li> + <li><a href="https://repology.org/project/fast-float/versions">More distributions</a></li> + </ul> + </article> + </div> + </div> + </section> + + <section id="users" class="section"> + <div class="container"> + <h2>Trusted by</h2> + <p class="section-lede">fast_float ships inside compilers, browsers, databases, and more.</p> + <ul class="users"> + <li><strong>GCC</strong> — backs <code>std::from_chars</code> since version 12</li> + <li><strong>Chromium</strong> — Chrome, Edge, and Opera</li> + <li><strong>WebKit</strong> — Safari</li> + <li><strong>Ladybird</strong> — independent browser engine</li> + <li><strong>DuckDB</strong> — in-process analytical database</li> + <li><strong>Apache Arrow</strong> — 2–3× faster number parsing</li> + <li><strong>ClickHouse</strong> — OLAP database</li> + <li><strong>MySQL</strong></li> + <li><strong>Boost.JSON</strong></li> + <li><strong>Blender</strong></li> + <li><strong>Google Jsonnet</strong></li> + </ul> + <p class="aside"> + Ports and bindings exist for + <a href="https://github.com/aldanor/fast-float-rust/">Rust</a>, + <a href="https://github.com/wrandelshofer/FastDoubleParser">Java</a>, + <a href="https://github.com/CarlVerret/csFastFloat">C#</a>, + <a href="https://github.com/kolemannix/ffc.h">C</a>, and + <a href="https://github.com/eddelbuettel/rcppfastfloat">R</a>. + </p> + </div> + </section> + + <section id="papers" class="section alt"> + <div class="container"> + <h2>The research behind it</h2> + <ul class="papers"> + <li> + Daniel Lemire, + <a href="https://arxiv.org/abs/2101.11408"><strong>Number Parsing at a Gigabyte per Second</strong></a>. + <em>Software: Practice and Experience</em> 51(8), 2021. + </li> + <li> + Noble Mushtak, Daniel Lemire, + <a href="https://arxiv.org/abs/2212.06644"><strong>Fast Number Parsing Without Fallback</strong></a>. + <em>Software: Practice and Experience</em> 53(7), 2023. + </li> + </ul> + </div> + </section> + </main> + + <footer class="site-footer"> + <div class="container footer-grid"> + <div> + <strong>fast_float</strong> + <p>Triple-licensed under Apache 2.0, MIT, and Boost. Use it however you like.</p> + </div> + <div> + <h4>Project</h4> + <ul> + <li><a href="https://github.com/fastfloat/fast_float">GitHub repository</a></li> + <li><a href="https://github.com/fastfloat/fast_float/releases">Releases</a></li> + <li><a href="https://github.com/fastfloat/fast_float/issues">Issue tracker</a></li> + <li><a href="https://github.com/fastfloat/fast_float/blob/main/SECURITY.md">Security policy</a></li> + </ul> + </div> + <div> + <h4>Learn more</h4> + <ul> + <li><a href="https://arxiv.org/abs/2101.11408">Number Parsing at a Gigabyte per Second</a></li> + <li><a href="https://www.youtube.com/watch?v=AVXgvlMeIm4">Go Systems 2020 talk</a></li> + <li><a href="https://github.com/fastfloat/fast_float/blob/main/README.md">README</a></li> + </ul> + </div> + </div> + <div class="container subfoot"> + <span>Current release <code data-version>v{{VERSION}}</code></span> + <span>Maintained by the <a href="https://github.com/fastfloat">fast_float</a> contributors.</span> + </div> + </footer> + + <script src="https://cdn.jsdelivr.net/gh/highlightjs/[email protected]/build/highlight.min.js"></script> + <script src="https://cdn.jsdelivr.net/gh/highlightjs/[email protected]/build/languages/cmake.min.js"></script> + <script src="https://cdn.jsdelivr.net/gh/highlightjs/[email protected]/build/languages/bash.min.js"></script> + <script> + // Promote each <pre data-lang="X"> to a Highlight.js language class + // on its inner <code>, then highlight everything. + document.querySelectorAll("pre[data-lang]").forEach(function (pre) { + var code = pre.querySelector("code"); + if (code && !code.className) code.className = "language-" + pre.dataset.lang; + }); + hljs.highlightAll(); + </script> + <script src="assets/app.js"></script> +</body> +</html> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fast_float-8.2.5/include/fast_float/ascii_number.h new/fast_float-8.2.6/include/fast_float/ascii_number.h --- old/fast_float-8.2.5/include/fast_float/ascii_number.h 2026-04-16 20:39:03.000000000 +0200 +++ new/fast_float-8.2.6/include/fast_float/ascii_number.h 2026-06-02 00:07:41.000000000 +0200 @@ -266,6 +266,21 @@ p)); // in rare cases, this will overflow, but that's ok p += 8; } + // Consume a remaining 4-7 digit run in a single SWAR step instead of + // byte-by-byte (reuses the existing 4-digit helpers). The parsed result is + // identical either way. Gated to clang: on gcc the extra 4-digit check + // regresses inputs whose remainder is shorter than 4 digits (it becomes pure + // overhead there); clang does not show that. +#if defined(__clang__) + if ((pend - p) >= 4) { + uint32_t const val4 = read4_to_u32(p); + if (is_made_of_four_digits_fast(val4)) { + i = i * 10000 + + parse_four_digits_unrolled(val4); // may overflow, that's ok + p += 4; + } + } +#endif } enum class parse_error { @@ -354,13 +369,36 @@ uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad) - while ((p != pend) && is_integer(*p)) { - // a multiplication by 10 is cheaper than an arbitrary integer - // multiplication - i = 10 * i + - uint64_t(*p - - UC('0')); // might overflow, we will handle the overflow later + // Straight-line unroll of the integer-part scan: most integer parts are + // 1-5 digits, so peeling the first iterations eliminates the loop back-edge + // for the common case. Semantics are identical to the original `while` loop: + // i = 10*i + digit, advancing p. + if ((p != pend) && is_integer(*p)) { + i = uint64_t(*p - UC('0')); ++p; + if ((p != pend) && is_integer(*p)) { + i = 10 * i + uint64_t(*p - UC('0')); + ++p; + if ((p != pend) && is_integer(*p)) { + i = 10 * i + uint64_t(*p - UC('0')); + ++p; + if ((p != pend) && is_integer(*p)) { + i = 10 * i + uint64_t(*p - UC('0')); + ++p; + if ((p != pend) && is_integer(*p)) { + i = 10 * i + uint64_t(*p - UC('0')); + ++p; + while ((p != pend) && is_integer(*p)) { + // a multiplication by 10 is cheaper than an arbitrary integer + // multiplication + i = 10 * i + + uint64_t(*p - UC('0')); // might overflow, handled later + ++p; + } + } + } + } + } } UC const *const end_of_integer_part = p; int64_t digit_count = int64_t(end_of_integer_part - start_digits); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fast_float-8.2.5/include/fast_float/float_common.h new/fast_float-8.2.6/include/fast_float/float_common.h --- old/fast_float-8.2.5/include/fast_float/float_common.h 2026-04-16 20:39:03.000000000 +0200 +++ new/fast_float-8.2.6/include/fast_float/float_common.h 2026-06-02 00:07:41.000000000 +0200 @@ -18,7 +18,7 @@ #define FASTFLOAT_VERSION_MAJOR 8 #define FASTFLOAT_VERSION_MINOR 2 -#define FASTFLOAT_VERSION_PATCH 5 +#define FASTFLOAT_VERSION_PATCH 6 #define FASTFLOAT_STRINGIZE_IMPL(x) #x #define FASTFLOAT_STRINGIZE(x) FASTFLOAT_STRINGIZE_IMPL(x) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fast_float-8.2.5/tests/CMakeLists.txt new/fast_float-8.2.6/tests/CMakeLists.txt --- old/fast_float-8.2.5/tests/CMakeLists.txt 2026-04-16 20:39:03.000000000 +0200 +++ new/fast_float-8.2.6/tests/CMakeLists.txt 2026-06-02 00:07:41.000000000 +0200 @@ -4,6 +4,10 @@ include(FetchContent) +# Some tests (the exhaustive sweeps) parallelize across std::thread. +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) + option(SYSTEM_DOCTEST "Use system copy of doctest" OFF) option(FASTFLOAT_SUPPLEMENTAL_TESTS "Run supplemental tests" ON) @@ -49,6 +53,7 @@ target_compile_options(${TEST_NAME} PUBLIC -Wsign-compare -Wshadow -Wwrite-strings -Wpointer-arith -Winit-self -Wconversion -Wsign-conversion) endif() target_link_libraries(${TEST_NAME} PUBLIC fast_float supplemental-data) + target_link_libraries(${TEST_NAME} PUBLIC Threads::Threads) if (NOT SYSTEM_DOCTEST) target_link_libraries(${TEST_NAME} PUBLIC doctest) else () diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fast_float-8.2.5/tests/exhaustive32.cpp new/fast_float-8.2.6/tests/exhaustive32.cpp --- old/fast_float-8.2.5/tests/exhaustive32.cpp 2026-04-16 20:39:03.000000000 +0200 +++ new/fast_float-8.2.6/tests/exhaustive32.cpp 2026-06-02 00:07:41.000000000 +0200 @@ -8,6 +8,8 @@ #include <iostream> #include <limits> #include <system_error> +#include <thread> +#include <vector> template <typename T> char *to_string(T d, char *buffer) { auto written = std::snprintf(buffer, 64, "%.*e", @@ -15,47 +17,59 @@ return buffer + written; } -void allvalues() { +// Checks a single 32-bit word (interpreted as a float); aborts on a mismatch. +void check_word(uint32_t word) { char buffer[64]; - for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) { - float v; - if ((w % 1048576) == 0) { - std::cout << "."; - std::cout.flush(); + float v; + memcpy(&v, &word, sizeof(v)); + + char const *string_end = to_string(v, buffer); + float result_value; + auto result = fast_float::from_chars(buffer, string_end, result_value); + // Starting with version 4.0 for fast_float, we return result_out_of_range + // if the value is either too small (too close to zero) or too large + // (effectively infinity). So std::errc::result_out_of_range is normal for + // well-formed input strings. + if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { + std::cerr << "parsing error ? " << buffer << std::endl; + abort(); + } + if (std::isnan(v)) { + if (!std::isnan(result_value)) { + std::cerr << "not nan" << buffer << std::endl; + abort(); } - uint32_t word = uint32_t(w); - memcpy(&v, &word, sizeof(v)); + } else if (copysign(1, result_value) != copysign(1, v)) { + std::cerr << "I got " << std::hexfloat << result_value + << " but I was expecting " << v << std::endl; + abort(); + } else if (result_value != v) { + std::cerr << "no match ? " << buffer << std::endl; + std::cout << "started with " << std::hexfloat << v << std::endl; + std::cout << "got back " << std::hexfloat << result_value << std::endl; + std::cout << std::dec; + abort(); + } +} - { - char const *string_end = to_string(v, buffer); - float result_value; - auto result = fast_float::from_chars(buffer, string_end, result_value); - // Starting with version 4.0 for fast_float, we return result_out_of_range - // if the value is either too small (too close to zero) or too large - // (effectively infinity). So std::errc::result_out_of_range is normal for - // well-formed input strings. - if (result.ec != std::errc() && - result.ec != std::errc::result_out_of_range) { - std::cerr << "parsing error ? " << buffer << std::endl; - abort(); - } - if (std::isnan(v)) { - if (!std::isnan(result_value)) { - std::cerr << "not nan" << buffer << std::endl; - abort(); - } - } else if (copysign(1, result_value) != copysign(1, v)) { - std::cerr << "I got " << std::hexfloat << result_value - << " but I was expecting " << v << std::endl; - abort(); - } else if (result_value != v) { - std::cerr << "no match ? " << buffer << std::endl; - std::cout << "started with " << std::hexfloat << v << std::endl; - std::cout << "got back " << std::hexfloat << result_value << std::endl; - std::cout << std::dec; - abort(); +// Sweeps the whole 2^32 float space, split across hardware threads (the values +// are independent); check_word() aborts on the first mismatch. +void allvalues() { + unsigned int nthreads = std::thread::hardware_concurrency(); + if (nthreads == 0) { + nthreads = 1; + } + std::vector<std::thread> workers; + workers.reserve(nthreads); + for (unsigned int t = 0; t < nthreads; t++) { + workers.emplace_back([t, nthreads]() { + for (uint64_t w = t; w <= 0xFFFFFFFF; w += nthreads) { + check_word(uint32_t(w)); } - } + }); + } + for (std::thread &worker : workers) { + worker.join(); } std::cout << std::endl; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fast_float-8.2.5/tests/exhaustive32_64.cpp new/fast_float-8.2.6/tests/exhaustive32_64.cpp --- old/fast_float-8.2.5/tests/exhaustive32_64.cpp 2026-04-16 20:39:03.000000000 +0200 +++ new/fast_float-8.2.6/tests/exhaustive32_64.cpp 2026-06-02 00:07:41.000000000 +0200 @@ -1,6 +1,7 @@ #include "fast_float/fast_float.h" +#include <atomic> #include <cassert> #include <cmath> #include <cstdio> @@ -9,6 +10,8 @@ #include <limits> #include <string> #include <system_error> +#include <thread> +#include <vector> template <typename T> char *to_string(T d, char *buffer) { auto written = std::snprintf(buffer, 64, "%.*e", @@ -45,25 +48,38 @@ return true; } +// Sweeps the whole 2^32 float space (widened to double), split across hardware +// threads (the values are independent); stops at the first mismatch. void all_32bit_values() { - char buffer[64]; - for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) { - float v32; - if ((w % 1048576) == 0) { - std::cout << "."; - std::cout.flush(); - } - uint32_t word = uint32_t(w); - memcpy(&v32, &word, sizeof(v32)); - double v = v32; - - { - char const *string_end = to_string(v, buffer); - std::string s(buffer, size_t(string_end - buffer)); - if (!basic_test_64bit(s, v)) { - return; + unsigned int nthreads = std::thread::hardware_concurrency(); + if (nthreads == 0) { + nthreads = 1; + } + std::atomic<bool> ok{true}; + std::vector<std::thread> workers; + workers.reserve(nthreads); + for (unsigned int t = 0; t < nthreads; t++) { + workers.emplace_back([t, nthreads, &ok]() { + char buffer[64]; + for (uint64_t w = t; + w <= 0xFFFFFFFF && ok.load(std::memory_order_relaxed); + w += nthreads) { + float v32; + uint32_t word = uint32_t(w); + memcpy(&v32, &word, sizeof(v32)); + double v = v32; + + char const *string_end = to_string(v, buffer); + std::string s(buffer, size_t(string_end - buffer)); + if (!basic_test_64bit(s, v)) { + ok.store(false, std::memory_order_relaxed); + return; + } } - } + }); + } + for (std::thread &worker : workers) { + worker.join(); } std::cout << std::endl; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/fast_float-8.2.5/tests/exhaustive32_midpoint.cpp new/fast_float-8.2.6/tests/exhaustive32_midpoint.cpp --- old/fast_float-8.2.5/tests/exhaustive32_midpoint.cpp 2026-04-16 20:39:03.000000000 +0200 +++ new/fast_float-8.2.6/tests/exhaustive32_midpoint.cpp 2026-06-02 00:07:41.000000000 +0200 @@ -1,5 +1,6 @@ #include "fast_float/fast_float.h" +#include <atomic> #include <cassert> #include <cmath> #include <cstdio> @@ -7,6 +8,8 @@ #include <iostream> #include <limits> #include <stdexcept> +#include <thread> +#include <vector> #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) // Anything at all that is related to cygwin, msys and so forth will @@ -74,84 +77,105 @@ } } -bool allvalues() { +// Checks a single 32-bit word (interpreted as a float). Returns true if the +// parser agrees with the reference, false (after logging) on a mismatch. +bool check_word(uint32_t word) { char buffer[64]; - for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) { - float v; - if ((w % 1048576) == 0) { - std::cout << "."; - std::cout.flush(); + float v; + memcpy(&v, &word, sizeof(v)); + if (!std::isfinite(v)) { + return true; + } + float nextf = std::nextafterf(v, INFINITY); + if (copysign(1, v) != copysign(1, nextf)) { + return true; + } + if (!std::isfinite(nextf)) { + return true; + } + double v1{v}; + assert(float(v1) == v); + double v2{nextf}; + assert(float(v2) == nextf); + double midv{v1 + (v2 - v1) / 2}; + float expected_midv = float(midv); + + char const *string_end = to_string(midv, buffer); + float str_answer; + strtof_from_string(buffer, str_answer); + + float result_value; + auto result = fast_float::from_chars(buffer, string_end, result_value); + // Starting with version 4.0 for fast_float, we return result_out_of_range + // if the value is either too small (too close to zero) or too large + // (effectively infinity). So std::errc::result_out_of_range is normal for + // well-formed input strings. + if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { + std::cerr << "parsing error ? " << buffer << std::endl; + return false; + } + if (std::isnan(v)) { + if (!std::isnan(result_value)) { + std::cerr << "not nan" << buffer << std::endl; + std::cerr << "v " << std::hexfloat << v << std::endl; + std::cerr << "v2 " << std::hexfloat << v2 << std::endl; + std::cerr << "midv " << std::hexfloat << midv << std::endl; + std::cerr << "expected_midv " << std::hexfloat << expected_midv + << std::endl; + return false; } - uint32_t word = uint32_t(w); - memcpy(&v, &word, sizeof(v)); - if (std::isfinite(v)) { - float nextf = std::nextafterf(v, INFINITY); - if (copysign(1, v) != copysign(1, nextf)) { - continue; - } - if (!std::isfinite(nextf)) { - continue; - } - double v1{v}; - assert(float(v1) == v); - double v2{nextf}; - assert(float(v2) == nextf); - double midv{v1 + (v2 - v1) / 2}; - float expected_midv = float(midv); - - char const *string_end = to_string(midv, buffer); - float str_answer; - strtof_from_string(buffer, str_answer); - - float result_value; - auto result = fast_float::from_chars(buffer, string_end, result_value); - // Starting with version 4.0 for fast_float, we return result_out_of_range - // if the value is either too small (too close to zero) or too large - // (effectively infinity). So std::errc::result_out_of_range is normal for - // well-formed input strings. - if (result.ec != std::errc() && - result.ec != std::errc::result_out_of_range) { - std::cerr << "parsing error ? " << buffer << std::endl; - return false; - } - if (std::isnan(v)) { - if (!std::isnan(result_value)) { - std::cerr << "not nan" << buffer << std::endl; - std::cerr << "v " << std::hexfloat << v << std::endl; - std::cerr << "v2 " << std::hexfloat << v2 << std::endl; - std::cerr << "midv " << std::hexfloat << midv << std::endl; - std::cerr << "expected_midv " << std::hexfloat << expected_midv - << std::endl; - return false; + } else if (copysign(1, result_value) != copysign(1, v)) { + std::cerr << buffer << std::endl; + std::cerr << "v " << std::hexfloat << v << std::endl; + std::cerr << "v2 " << std::hexfloat << v2 << std::endl; + std::cerr << "midv " << std::hexfloat << midv << std::endl; + std::cerr << "expected_midv " << std::hexfloat << expected_midv + << std::endl; + std::cerr << "I got " << std::hexfloat << result_value + << " but I was expecting " << v << std::endl; + return false; + } else if (result_value != str_answer) { + std::cerr << "no match ? " << buffer << std::endl; + std::cerr << "v " << std::hexfloat << v << std::endl; + std::cerr << "v2 " << std::hexfloat << v2 << std::endl; + std::cerr << "midv " << std::hexfloat << midv << std::endl; + std::cerr << "expected_midv " << std::hexfloat << expected_midv + << std::endl; + std::cout << "started with " << std::hexfloat << midv << std::endl; + std::cout << "round down to " << std::hexfloat << str_answer << std::endl; + std::cout << "got back " << std::hexfloat << result_value << std::endl; + std::cout << std::dec; + return false; + } + return true; +} + +// Sweeps the whole 2^32 float space, split across hardware threads (the values +// are independent). Returns false as soon as any word mismatches. +bool allvalues() { + unsigned int nthreads = std::thread::hardware_concurrency(); + if (nthreads == 0) { + nthreads = 1; + } + std::atomic<bool> ok{true}; + std::vector<std::thread> workers; + workers.reserve(nthreads); + for (unsigned int t = 0; t < nthreads; t++) { + workers.emplace_back([t, nthreads, &ok]() { + for (uint64_t w = t; + w <= 0xFFFFFFFF && ok.load(std::memory_order_relaxed); + w += nthreads) { + if (!check_word(uint32_t(w))) { + ok.store(false, std::memory_order_relaxed); + return; } - } else if (copysign(1, result_value) != copysign(1, v)) { - std::cerr << buffer << std::endl; - std::cerr << "v " << std::hexfloat << v << std::endl; - std::cerr << "v2 " << std::hexfloat << v2 << std::endl; - std::cerr << "midv " << std::hexfloat << midv << std::endl; - std::cerr << "expected_midv " << std::hexfloat << expected_midv - << std::endl; - std::cerr << "I got " << std::hexfloat << result_value - << " but I was expecting " << v << std::endl; - return false; - } else if (result_value != str_answer) { - std::cerr << "no match ? " << buffer << std::endl; - std::cerr << "v " << std::hexfloat << v << std::endl; - std::cerr << "v2 " << std::hexfloat << v2 << std::endl; - std::cerr << "midv " << std::hexfloat << midv << std::endl; - std::cerr << "expected_midv " << std::hexfloat << expected_midv - << std::endl; - std::cout << "started with " << std::hexfloat << midv << std::endl; - std::cout << "round down to " << std::hexfloat << str_answer - << std::endl; - std::cout << "got back " << std::hexfloat << result_value << std::endl; - std::cout << std::dec; - return false; } - } + }); } - std::cout << std::endl; - return true; + for (std::thread &worker : workers) { + worker.join(); + } + return ok.load(); } inline void Assert(bool Assertion) {
