Repository navigation
Conversation
- Exp: Floor( fx ) is never above fx, so the MaskedDecrement fix-up after it was a no-op (left over from a truncation-based floor). - Log: compute the exponent in the integer domain (i - 126, int masked decrement) and convert once, instead of convert, +1 and a float masked decrement. The values are small integers, so the result is identical. - Pow: Log's own a > 0 mask is redundant under Pow's mask, use an unmasked log internally.
- Log: split x into 2^e * m with m in [sqrt(0.5), sqrt(2)) by subtracting the bits of sqrt(0.5) (arithmetic shift for e, low bits + sqrt(0.5)'s bits for m). This gives exactly the e and m of the [0.5, 1) mantissa doubled below sqrt(0.5), without the compare, masked add and masked decrement. - Exp: fx is an integer in [-127, 128], so fx + 2^23 + 127 holds fx + 127 in its low mantissa bits; shift that instead of converting and adding 127.
Without SSE4.1's round instruction FS::Floor needs an int round trip plus overflow handling and a fix-up. The clamped exponent here is within [-127, 128], so round through int and fix up without the overflow select.
This was referenced Oct 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The generic
FS::Exp,FS::LogandFS::Pow(ToolSet/Generic/Functions.h) do work that either never changes the result or can be done more cheaply. Every feature set uses this code, except relaxed SCALAR, which callsstd::.ExpfollowsFloor( fx )withMaskedDecrement( flr > fx, flr ). A floor is never above its input, so this never fires. It is left over from the truncation-based floor insse_mathfun.Loggets the [0.5, 1) mantissa with an and/or, compares it with sqrt(0.5), doubles it with a masked add, and fixes the exponent with a convert, a +1 and a float masked decrement.Expbuilds 2^n with a float to int convert and an int add.Powzeroesvalue <= 0lanes, and theLoginside it does the same again.FS::Floorneeds an int round trip plus a select for inputs outside int range.Expnever gives it such inputs.FastNoise2 calls
Powfor its Minkowski distance and its PowFloat node.Change
Three commits, each giving the same bits as before:
Exp's floor fix-up.Logcomputes the exponent in the int domain and converts once.Powcallsimpl::LogUnmasked, the same log without thea > 0mask, because its own mask already covers those lanes.Logsubtracts the bits of sqrt(0.5) (0x3f3504f3) from the input's bits. An arithmetic shift of that gives the exponent, and the low 23 bits plus0x3f3504f3give the mantissa in [sqrt(0.5), sqrt(2)). These are the same e and m as the old compare-and-double path.Exp: after the clamp and floor,fxis an integer in [-127, 128]. Sofx + 2^23 + 127holdsfx + 127in its low mantissa bits, and shifting its bits left by 23 gives 2^n.Exprounds through int and fixes up with a masked decrement, withoutFS::Floor's range select. This only applies to SSE2 without SSE4.1.With clang-cl 23, FastNoise2's 2D PowFloat function goes from 254 to 220 instructions on SSE2, 228 to 210 on SSE4.1, 215 to 197 on AVX2 and 193 to 179 on AVX512. Each one runs
Powon two native registers.Effect
I built FastNoise2 master (8c2d080) against this branch and against FastSIMD master, with the same compiler, and timed both on a Ryzen 9 7950X. Both builds are loaded in one process and timed interleaved on one pinned core. Each figure is the median of 3 processes with alternating order. The Apple M1 runs are unpinned.
The Pow cases are the four Minkowski distance nodes (DistanceToPoint and CellularValue/Distance/Lookup, with a fixed and a node-driven P where available) plus PowFloat, in 2D, 3D and 4D. The controls are the Euclidean CellularValue/Distance/Lookup cases, which compile into the same functions but never call
Pow, plus PowInt. Numbers are new/old time, geomean per feature set:On the Apple M1 with Homebrew clang 23, NEON is 0.928 and AARCH64 0.930, with controls at 0.995.
Earlier measurements on a FastNoise2 development branch, on a busier machine, show the same pattern per commit. With clang-cl 23:
With MSVC 19.51, all three together measured 0.88-0.89 on every feature set.
Testing
f32 exp,f32 log,f32 powandf32 pow zero base. In relaxed mode they check againststd::within 8/8/256 ULP. The non-relaxedf32 exp/log/pow full rangetests compare every feature set with SCALAR bit for bit. That is what checks the SSE2-only floor path againstFS::Floor.f32 expandf32 exp full rangethen fail on SSE2 at every width.Exp,LogandPow( |x|, 2 ), plus 2^28 randomPowpairs, at native and double width./fp:preciseor-ffp-contract=off) there are no differences on SCALAR, SSE2, SSE4.1, AVX2 and AVX512 with clang-cl 23, MSVC 19.51 and GCC 16. There are also none on NEON/AARCH64 with M1 clang 23.Loginputs and 27,454Pow( |x|, 2 )inputs differ by 1 ulp because MSVC fuses the multiply-adds differently. In the cases I looked at, the old and new results sat about half an ulp either side of the exact value./fp:fastor-ffast-math, and there some cases round differently, all below 1e-3 relative:CalcDistanceswitch out of line, so changing the Minkowski branch changes how it compiles the other branches.-fno-associative-math(GCC 16, M1 Homebrew clang 23) or/fp:precise(MSVC 19.51), FastNoise2's output was bit-identical again. So the differences come from fast-math reordering of the code around these functions, not from the functions themselves.Loghalf of commit 2 triggers them. Without it, those compilers give bit-identical FastNoise2 output, but the Pow cases run 3-6% slower than with it.Other PRs
Functions.h, in a different function.