This commit is contained in:
Daniel Lemire 2026-05-27 12:43:38 -04:00
parent 05087a303d
commit baffc57197
7 changed files with 1049 additions and 0 deletions

90
.github/workflows/pages.yml vendored Normal file
View File

@ -0,0 +1,90 @@
name: Deploy GitHub Pages
# Builds the docs/ site, substitutes the latest release tag into the HTML,
# then deploys the result to GitHub Pages. Runs on:
# - any push to main that touches docs/, this workflow, or CMakeLists.txt
# - every published release (so a new tag refreshes the site)
# - manual dispatch from the Actions tab
on:
push:
branches: [main]
paths:
- "docs/**"
- ".github/workflows/pages.yml"
- "CMakeLists.txt"
release:
types: [published]
workflow_dispatch:
# Required permissions for the deploy-pages action.
permissions:
contents: read
pages: write
id-token: write
# Avoid concurrent deploys; let the latest one win.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
name: Build site
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
# Needed so the git-tag fallback can find release tags.
fetch-depth: 0
fetch-tags: true
- name: Resolve latest release version
id: version
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
# Prefer the latest published GitHub release.
tag="$(gh release view --repo ${{ github.repository }} --json tagName --jq .tagName 2>/dev/null || true)"
if [ -z "${tag:-}" ]; then
# Fall back to the newest semver-looking git tag.
tag="$(git tag --sort=-v:refname | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1 || true)"
fi
if [ -z "${tag:-}" ]; then
# Last-resort fallback: read version from CMakeLists.txt.
tag="v$(grep -E 'project\(fast_float VERSION' CMakeLists.txt | sed -E 's/.*VERSION ([0-9.]+).*/\1/')"
fi
# Strip a leading "v" — the template uses bare semver after "v".
version="${tag#v}"
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
echo "version=${version}" >> "$GITHUB_OUTPUT"
echo "Resolved version: ${version} (tag ${tag})"
- name: Substitute version into HTML
run: |
set -euo pipefail
version='${{ steps.version.outputs.version }}'
# In-place replace every {{VERSION}} occurrence under docs/.
find docs -type f \( -name '*.html' -o -name '*.md' -o -name '*.css' -o -name '*.js' \) \
-exec sed -i "s/{{VERSION}}/${version}/g" {} +
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: docs
deploy:
name: Deploy to GitHub Pages
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy
id: deployment
uses: actions/deploy-pages@v4

0
docs/.nojekyll Normal file
View File

25
docs/README.md Normal file
View File

@ -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.

116
docs/assets/app.js Normal file
View File

@ -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;
}
})();

15
docs/assets/logo.svg Normal file
View File

@ -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>

After

Width:  |  Height:  |  Size: 663 B

454
docs/assets/style.css Normal file
View File

@ -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; }
}

349
docs/index.html Normal file
View File

@ -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/cdn-release@11.10.0/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 236, plus <code>bool</code>. The same fast, allocation-free interface.</p>
</article>
<article class="card">
<h3>Unicode &amp; 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 &lt;iostream&gt;
#include &lt;string&gt;
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 &lt;&lt; "parsing failure\n";
return EXIT_FAILURE;
}
std::cout &lt;&lt; "parsed the number " &lt;&lt; result &lt;&lt; '\n';
}</code></pre>
<h3>Integers in any base (236)</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> — 23× 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/cdn-release@11.10.0/build/highlight.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.10.0/build/languages/cmake.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.10.0/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>