{"article":{"slug":"superoptimizing-an-fpu-emulator","title":"Superoptimizing an FPU Emulator","subtitle":null,"summary":"purplesyringa wanted a branch-free way to compute the lowest set bit of a float for a floating-point emulator library, and instead of hand-deriving it wrote a small superoptimizer that searched instruction sequences and found a surprisingly clever rewrite.","content_type":"blog_post","language":"en","canonical_url":"https://purplesyringa.moe/blog/superoptimizing-an-fpu-emulator/","author":{"name":"purplesyringa","url":"https://purplesyringa.moe/","person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"purplesyringa's blog","url":"https://purplesyringa.moe/blog/","listing_slug":null,"listing":null},"topics":[{"name":"Compilers","slug":"compilers","url":"https://listedarticles.com/topics/compilers"},{"name":"Performance","slug":"performance","url":"https://listedarticles.com/topics/performance"},{"name":"Rust","slug":"rust","url":"https://listedarticles.com/topics/rust"}],"about_listings":[],"cover_image_url":null,"license":"all-rights-reserved","word_count":974,"reading_minutes":4,"published_at":"2026-10-16T00:00:00.000Z","added_at":"2026-10-18T05:16:59.891Z","updated_at":"2026-10-18T05:16:59.891Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/superoptimizing-an-fpu-emulator","markdown_url":"https://listedarticles.com/articles/superoptimizing-an-fpu-emulator.md","example":false,"citation":"purplesyringa, purplesyringa's blog. \"Superoptimizing an FPU Emulator.\" 16 Oct 2026. https://purplesyringa.moe/blog/superoptimizing-an-fpu-emulator/ (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://purplesyringa.moe/blog/superoptimizing-an-fpu-emulator/"},"body_markdown":"> **Superoptimization** is the process where a compiler automatically finds the optimal sequence for a loop-free sequence of instructions.\n>\n> – [Wikipedia](https://en.wikipedia.org/wiki/Superoptimization)\n\nCompilers are good at making mechanical transformations that make code faster. Unfortunately, most of them are not as good at non-trivial reasoning, so they can’t invent a completely novel approach to solving your problem.\n\nWhen designing a library for floating-point calculations, I had to implement a function that takes a float f and produces a float corresponding to the lowest set bit in f. For example, if f=1.01101⋅210 in binary, it would return 0.00001⋅210=25.\n\nThis can be implemented with bit twiddling, but the straightforward algorithm breaks for powers of two, so I had to insert an `if`:\n\n```\nif f32::from_bits(x.to_bits() & !f32::MANTISSA_MASK) == x { // is x a power of two?\n    return x; // x itself is the lowest bit\n} else {\n    return x - f32::from_bits(x.to_bits() & (x.to_bits() - 1)); // x, minus x without lowest bit\n}\n```\n\nThis `if` really annoyed me. It’s slowed down for the common case, and branches could be mispredicted. I wanted to find a formula that automatically handled powers of two, but try as I might, I couldn’t find anything convincingly optimal.\n\nSo I made a non-orthodox play.\n\n### Brute-force\n\nWhat if there was a way to find such a formula automatically? No, not by spawning an LLM swarm, but by brute-forcing all possible formulas. LLMs can miss stuff or overindex on popular patterns; brute-force can’t.\n\nBut how do we check if a formula is correct? Theorem provers are too slow, but we can design a few representative test cases and hope that whatever formula we find is correct. And if it has a counterexample, just add it to the list of test cases and repeat.\n\nThis is not as insane as it sounds: by this moment I had already implemented an algorithm with 6 operations, so I just needed to iterate over formulas with 5 ops or fewer. I knew that the following operations could be useful:\n\n- Integer addition/subtraction and bitwise AND/OR/XOR for bit tricks.- Floating-point subtraction as the last operation.- Maybe comparisons?\n\nThere are 65=7776 ways to choose 5 operations, each of which can be one of 6 variants, and [C5=42 possible parentheses placements](https://en.wikipedia.org/wiki/Catalan_number). That’s not a lot!\n\nBut we also need to choose the inputs to each operation, which can be either the input value x or some constant. I guessed that we’ll only need constants that cover various fields of the `float` binary representation, and not completely random ones, reducing the list to 64 different constants. That’s 656 combinations, which sounds like a lot, but in practice we can cull many of them.\n\n### Search\n\nTo implement the search efficiently, I build up the formula set inductively.\n\nFormulas with 0 operations are just x and constants; formulas with n operations can be found by combining all pairs of formulas with k and n−1−k operations:\n\n```\nfor n_ops in 1..N_OPS {\n    let mut new_exprs = FxHashSet::default();\n    for a_ops in 0..n_ops {\n        let b_ops = n_ops - 1 - a_ops;\n        let a_exprs = &exprs_per_op_count[a_ops];\n        let b_exprs = &exprs_per_op_count[b_ops];\n        for a_expr in a_exprs {\n            for b_expr in b_exprs {\n                for op in OPS {\n                    // New expression: `a_expr op b_expr`\n                    new_exprs.insert(Expr {\n                        values: core::array::from_fn(|i| {\n                            op(a_expr.values[i], b_expr.values[i])\n                        }),\n                    });\n                }\n            }\n        }\n    }\n}\n```\n\nInduction allows us to prune duplicates. For example, (x+1)+2 and (x+2)+1 are different expressions, but they return the same values. Keeping just one expression for each set of outputs allows further iterations to complete faster. This is the reason why brute-force can complete in just 4 minutes! (I’m sure it can be optimized further, but I preferred watching YouTube over optimizing a one-off script.)\n\nTo save time and memory, I avoid deduplication on the last iteration and output all matching candidates. Memory usage matters because I had 20 test cases by this point, so I had to store 80 bytes per expression, and there are *a lot* of expressions.\n\n### Results\n\nSo, did this exhaustive search find anything interesting? Yes! There were several formulas with 5 operations, e.g. this one:\n\n```\nreturn x - f32::from_bits(x.to_bits() & (\n    (f32::EXPONENT_MASK - (x | f32::EXPONENT_MASK)) ^ !f32::EXPONENT_MASK\n));\n```\n\nI couldn’t find formulas with 4 operations, but this approach has a limitation: it doesn’t take into account that calculations can be reused. So, for example, if `x & 1` is used in two places, it will count that as two operations, not one. I was hoping that one of the 5-op formulas could actually be expressed as 4 operations via reuse, and after manually checking every formula, the very last one turned out to have this property! Here’s what it looks like:\n\n```\nlet a = x.to_bits() & !f32::MANTISSA_MASK; // x without mantissa bits\nreturn f32::from_bits(x.to_bits() & (a - x.to_bits())) - f32::from_bits(a);\n```\n\nIt behaves a little differently for powers of two – it flips the sign bit, which I don’t care about. And the logic doesn’t make sense at first glance, and yet it works.\n\nThe reason why only this formula can reuse `a` is the subexpression `a - x.to_bits()`. It’s equivalent to `-(x & f32::MANTISSA_MASK)`, which is how other formulas refer to it. All formulas compute `x & !f32::MANTISSA_MASK` and `x & f32::MANTISSA_MASK`, but this one rewrites the latter as `x - (x & f32::MANTISSA_MASK)`, and then merges that with negation to save one operation!\n\nI was flabbergasted when I realized it found such a clever rewrite. You don’t need an LLM to be amazed by what computers can invent! And it doesn’t destroy industries.\n\nAnyway, this code now [lives in my work-in-progress FPU emulator](https://github.com/purplesyringa/fpu-v/blob/d3c3f72c691698e5095d259a79610e5293911651/src/ops/multiplication.rs#L485) and I’m proud of it.\n","body_html":"<blockquote><p><strong>Superoptimization</strong> is the process where a compiler automatically finds the optimal sequence for a loop-free sequence of instructions.</p>\n<p>– <a href=\"https://en.wikipedia.org/wiki/Superoptimization\" rel=\"nofollow ugc noopener\">Wikipedia</a></p></blockquote>\n<p>Compilers are good at making mechanical transformations that make code faster. Unfortunately, most of them are not as good at non-trivial reasoning, so they can’t invent a completely novel approach to solving your problem.</p>\n<p>When designing a library for floating-point calculations, I had to implement a function that takes a float f and produces a float corresponding to the lowest set bit in f. For example, if f=1.01101⋅210 in binary, it would return 0.00001⋅210=25.</p>\n<p>This can be implemented with bit twiddling, but the straightforward algorithm breaks for powers of two, so I had to insert an <code>if</code>:</p>\n<pre><code>if f32::from_bits(x.to_bits() &amp; !f32::MANTISSA_MASK) == x { // is x a power of two?\n    return x; // x itself is the lowest bit\n} else {\n    return x - f32::from_bits(x.to_bits() &amp; (x.to_bits() - 1)); // x, minus x without lowest bit\n}</code></pre>\n<p>This <code>if</code> really annoyed me. It’s slowed down for the common case, and branches could be mispredicted. I wanted to find a formula that automatically handled powers of two, but try as I might, I couldn’t find anything convincingly optimal.</p>\n<p>So I made a non-orthodox play.</p>\n<h3 id=\"brute-force\">Brute-force</h3>\n<p>What if there was a way to find such a formula automatically? No, not by spawning an LLM swarm, but by brute-forcing all possible formulas. LLMs can miss stuff or overindex on popular patterns; brute-force can’t.</p>\n<p>But how do we check if a formula is correct? Theorem provers are too slow, but we can design a few representative test cases and hope that whatever formula we find is correct. And if it has a counterexample, just add it to the list of test cases and repeat.</p>\n<p>This is not as insane as it sounds: by this moment I had already implemented an algorithm with 6 operations, so I just needed to iterate over formulas with 5 ops or fewer. I knew that the following operations could be useful:</p>\n<ul><li>Integer addition/subtraction and bitwise AND/OR/XOR for bit tricks.- Floating-point subtraction as the last operation.- Maybe comparisons?</li></ul>\n<p>There are 65=7776 ways to choose 5 operations, each of which can be one of 6 variants, and <a href=\"https://en.wikipedia.org/wiki/Catalan_number\" rel=\"nofollow ugc noopener\">C5=42 possible parentheses placements</a>. That’s not a lot!</p>\n<p>But we also need to choose the inputs to each operation, which can be either the input value x or some constant. I guessed that we’ll only need constants that cover various fields of the <code>float</code> binary representation, and not completely random ones, reducing the list to 64 different constants. That’s 656 combinations, which sounds like a lot, but in practice we can cull many of them.</p>\n<h3 id=\"search\">Search</h3>\n<p>To implement the search efficiently, I build up the formula set inductively.</p>\n<p>Formulas with 0 operations are just x and constants; formulas with n operations can be found by combining all pairs of formulas with k and n−1−k operations:</p>\n<pre><code>for n_ops in 1..N_OPS {\n    let mut new_exprs = FxHashSet::default();\n    for a_ops in 0..n_ops {\n        let b_ops = n_ops - 1 - a_ops;\n        let a_exprs = &amp;exprs_per_op_count[a_ops];\n        let b_exprs = &amp;exprs_per_op_count[b_ops];\n        for a_expr in a_exprs {\n            for b_expr in b_exprs {\n                for op in OPS {\n                    // New expression: `a_expr op b_expr`\n                    new_exprs.insert(Expr {\n                        values: core::array::from_fn(|i| {\n                            op(a_expr.values[i], b_expr.values[i])\n                        }),\n                    });\n                }\n            }\n        }\n    }\n}</code></pre>\n<p>Induction allows us to prune duplicates. For example, (x+1)+2 and (x+2)+1 are different expressions, but they return the same values. Keeping just one expression for each set of outputs allows further iterations to complete faster. This is the reason why brute-force can complete in just 4 minutes! (I’m sure it can be optimized further, but I preferred watching YouTube over optimizing a one-off script.)</p>\n<p>To save time and memory, I avoid deduplication on the last iteration and output all matching candidates. Memory usage matters because I had 20 test cases by this point, so I had to store 80 bytes per expression, and there are <em>a lot</em> of expressions.</p>\n<h3 id=\"results\">Results</h3>\n<p>So, did this exhaustive search find anything interesting? Yes! There were several formulas with 5 operations, e.g. this one:</p>\n<pre><code>return x - f32::from_bits(x.to_bits() &amp; (\n    (f32::EXPONENT_MASK - (x | f32::EXPONENT_MASK)) ^ !f32::EXPONENT_MASK\n));</code></pre>\n<p>I couldn’t find formulas with 4 operations, but this approach has a limitation: it doesn’t take into account that calculations can be reused. So, for example, if <code>x &amp; 1</code> is used in two places, it will count that as two operations, not one. I was hoping that one of the 5-op formulas could actually be expressed as 4 operations via reuse, and after manually checking every formula, the very last one turned out to have this property! Here’s what it looks like:</p>\n<pre><code>let a = x.to_bits() &amp; !f32::MANTISSA_MASK; // x without mantissa bits\nreturn f32::from_bits(x.to_bits() &amp; (a - x.to_bits())) - f32::from_bits(a);</code></pre>\n<p>It behaves a little differently for powers of two – it flips the sign bit, which I don’t care about. And the logic doesn’t make sense at first glance, and yet it works.</p>\n<p>The reason why only this formula can reuse <code>a</code> is the subexpression <code>a - x.to_bits()</code>. It’s equivalent to <code>-(x &amp; f32::MANTISSA_MASK)</code>, which is how other formulas refer to it. All formulas compute <code>x &amp; !f32::MANTISSA_MASK</code> and <code>x &amp; f32::MANTISSA_MASK</code>, but this one rewrites the latter as <code>x - (x &amp; f32::MANTISSA_MASK)</code>, and then merges that with negation to save one operation!</p>\n<p>I was flabbergasted when I realized it found such a clever rewrite. You don’t need an LLM to be amazed by what computers can invent! And it doesn’t destroy industries.</p>\n<p>Anyway, this code now <a href=\"https://github.com/purplesyringa/fpu-v/blob/d3c3f72c691698e5095d259a79610e5293911651/src/ops/multiplication.rs#L485\" rel=\"nofollow ugc noopener\">lives in my work-in-progress FPU emulator</a> and I’m proud of it.</p>","headings":[{"level":3,"text":"Brute-force","id":"brute-force"},{"level":3,"text":"Search","id":"search"},{"level":3,"text":"Results","id":"results"}]}}