├── .github └── workflows │ └── test.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── foundry.toml ├── script └── PrintGaussian.s.sol ├── src ├── Gaussian.sol └── GaussianYul.sol └── test └── Gaussian.t.sol /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: workflow_dispatch 4 | 5 | env: 6 | FOUNDRY_PROFILE: ci 7 | 8 | jobs: 9 | check: 10 | strategy: 11 | fail-fast: true 12 | 13 | name: Foundry project 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | submodules: recursive 19 | 20 | - name: Install Foundry 21 | uses: foundry-rs/foundry-toolchain@v1 22 | with: 23 | version: nightly 24 | 25 | - name: Run Forge build 26 | run: | 27 | forge --version 28 | forge build --sizes 29 | id: build 30 | 31 | - name: Run Forge tests 32 | run: | 33 | forge test -vvv 34 | id: test 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiler files 2 | cache/ 3 | out/ 4 | 5 | # Ignores development broadcast logs 6 | !/broadcast 7 | /broadcast/*/31337/ 8 | /broadcast/**/dry-run/ 9 | 10 | # Docs 11 | docs/ 12 | 13 | # Dotenv file 14 | .env 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 5/9 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Solidity CDF 2 | 3 | A Solidity implementation of the CDF function with 1e-8 precision. 4 | 5 | The implementation uses a $(14, 5)$-term rational approximation optimized to minimize its maximum error with the Remez algorithm. 6 | See [`Gaussian`](https://github.com/fiveoutofnine/solidity-cdf/blob/main/src/Gaussian.sol)/[`GaussianYul`](https://github.com/fiveoutofnine/solidity-cdf/blob/main/src/GaussianYul.sol) for a more detailed explanation and breakdown of a few minor optimizations. 7 | 8 |
9 | View computed parameters 10 | 11 | ```py 12 | # Maximum absolute error: 1.3430230007294523e-08 13 | p = [ 14 | 1.0000000134302306273277303995065049273812887383708033070765535, 15 | -1.6270692420214419689048648107492650445124851868993666400546805, 16 | 0.95695939771557621630788382329318927876123532216447446035189629, 17 | -0.16633851257171462994968608289343417448516569184903674878490485, 18 | -0.045757328741294745592296128432121626122059153122475781868753213, 19 | -0.0037075449565245192403519588225698683637412864376254631738819151, 20 | 0.028488362762927522030907690912222875179502188416622942809751882, 21 | -0.016287529736941549594061740522247112596133132474157032579938495, 22 | 0.0048172203752504698927671605458443098230415330921338519326194087, 23 | -0.00087210950306797040922396209123138809770604994758407050613031373, 24 | 0.000099149714653716020389941915394734964728925581883441887433840514, 25 | -0.0000067602050744659632236787608312592389179948948837815547237917173, 26 | 0.0000002393840402106164843090318571199276741485022552134339717005062, 27 | -0.0000000028518992951670794908019473353232308246724639330742090849192205, 28 | ] 29 | 30 | q = [ 31 | 1.0, 32 | -0.49868824055465048831235903088578070819751012339484972855105241, 33 | 0.39420799004922720178799577859465503879202580871471965614479612, 34 | -0.097268947975024830829217859353973885910383757802529177945661066, 35 | 0.030267961461764185107683252708473682723390196231235004968537513, 36 | ] 37 | 38 | ``` 39 | 40 |
41 | 42 | ## Acknowledgements 43 | 44 | - [**Remco**](https://x.com/recmo) and his blog post [**Approximation Theory**](https://2π.com/22/approximation) for detailing how to compute an optimal polynomial/rational approximation. 45 | - [**philogy**](https://x.com/real_philogy) for open-sourcing [**Philogy/gud-ctf**](https://github.com/Philogy/gud-cdf) early and serving as a nice reference to view and test against. 46 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = "src" 3 | out = "out" 4 | libs = ['lib'] 5 | solc = "0.8.26" 6 | optimizer_runs = 1_000_000 7 | bytecode_hash = "none" 8 | 9 | [fmt] 10 | line_length = 200 11 | tab_width = 4 12 | bracket_spacing = true 13 | int_types = "long" 14 | func_attrs_with_params_multiline = false 15 | quote_style = "double" 16 | number_underscore = "thousands" 17 | -------------------------------------------------------------------------------- /script/PrintGaussian.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import { console } from "forge-std/Test.sol"; 5 | import { Script } from "forge-std/Script.sol"; 6 | 7 | import { Gaussian } from "src/Gaussian.sol"; 8 | import { GaussianYul } from "src/GaussianYul.sol"; 9 | 10 | /// @notice A helper script to log values of {Gaussian.cdf} to console for 11 | /// testing. 12 | contract PrintGaussianScript is Script { 13 | /// @notice Computes the CDF values for a few test cases and logs them to 14 | /// console. 15 | function run() public pure { 16 | console.log(Gaussian.cdf({ _x: 2e18, _mu: 0.75e18, _sigma: 2e18 })); 17 | console.log(GaussianYul.cdf({ _x: 2e18, _mu: 0.75e18, _sigma: 2e18 })); 18 | console.log(Gaussian.cdf({ _x: -2e18, _mu: 0.75e18, _sigma: 2e18 })); 19 | console.log(GaussianYul.cdf({ _x: -2e18, _mu: 0.75e18, _sigma: 2e18 })); 20 | console.log(Gaussian.cdf({ _x: 0, _mu: 0, _sigma: 1e18 })); 21 | console.log(GaussianYul.cdf({ _x: 0, _mu: 0, _sigma: 1e18 })); 22 | console.log(Gaussian.cdf({ _x: 1e18, _mu: 0, _sigma: 1e18 })); 23 | console.log(GaussianYul.cdf({ _x: 1e18, _mu: 0, _sigma: 1e18 })); 24 | console.log(Gaussian.cdf({ _x: 2e18, _mu: 0, _sigma: 1e18 })); 25 | console.log(GaussianYul.cdf({ _x: 2e18, _mu: 0, _sigma: 1e18 })); 26 | console.log(Gaussian.cdf({ _x: 5.342e18, _mu: 0, _sigma: 1e18 })); 27 | console.log(GaussianYul.cdf({ _x: 5.342e18, _mu: 0, _sigma: 1e18 })); 28 | console.log(Gaussian.cdf({ _x: 10e18, _mu: 0, _sigma: 1e18 })); 29 | console.log(GaussianYul.cdf({ _x: 10e18, _mu: 0, _sigma: 1e18 })); 30 | console.log(Gaussian.cdf({ _x: -1e18, _mu: 0, _sigma: 1e18 })); 31 | console.log(GaussianYul.cdf({ _x: -1e18, _mu: 0, _sigma: 1e18 })); 32 | console.log(Gaussian.cdf({ _x: -2e18, _mu: 0, _sigma: 1e18 })); 33 | console.log(GaussianYul.cdf({ _x: -2e18, _mu: 0, _sigma: 1e18 })); 34 | console.log(Gaussian.cdf({ _x: -5.342e18, _mu: 0, _sigma: 1e18 })); 35 | console.log(GaussianYul.cdf({ _x: -5.342e18, _mu: 0, _sigma: 1e18 })); 36 | console.log(Gaussian.cdf({ _x: -10e18, _mu: 0, _sigma: 1e18 })); 37 | console.log(GaussianYul.cdf({ _x: -10e18, _mu: 0, _sigma: 1e18 })); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Gaussian.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | library Gaussian { 5 | /// @notice Computes the cumulative distribution function (CDF) for an 6 | /// 18-decimal fixed-point numbers `_x`, `_mu`, and `_sigma` with 1e-8 7 | /// precision. 8 | /// @dev The function doesn't check for overflows and underflows and assumes 9 | /// the following bounds: `_x` ∈ [-1e23, 1e23], `_mu` ∈ [-1e20, 1e20], 10 | /// `_sigma` ∈ (0, 1e19]. 11 | /// @param _x The value to evaluate the CDF at, as an 18-decimal fixed-point 12 | /// number. 13 | /// @param _mu The mean of the distribution, as an 18-decimal fixed-point 14 | /// number. 15 | /// @param _sigma The standard deviation of the distribution, as an 16 | /// 18-decimal fixed-point number. 17 | /// @return The CDF value at `_x`, `_mu`, and `_sigma`, as an 18-decimal 18 | /// fixed-point number. 19 | function cdf(int256 _x, int256 _mu, uint256 _sigma) internal pure returns (uint256) { 20 | unchecked { 21 | _x -= _mu; 22 | 23 | int256 x; 24 | assembly { 25 | // Compute `_x / (_sigma * sqrt(2))` and scale it to 2**96 base. 26 | // ``` 27 | // (2**96 / WAD) * (_x * WAD / (_sigma * sqrt(2) * WAD / WAD)) 28 | // = _x * 2**96 / (_sigma * sqrt(2)) 29 | // = _x * 0xb504f333f9de6484597d89b3 / _sigma. 30 | // ``` 31 | x := sdiv(mul(_x, 0xb504f333f9de6484597d89b3), _sigma) 32 | } 33 | 34 | // Divide result by 2. 35 | return erfc(x) >> 1; 36 | } 37 | } 38 | 39 | /// @notice Computes the complementary error function (erfc) for an 40 | /// 18-decimal fixed-point number `_x` scaled to 2**96 base with 2e-8 41 | /// precision. 42 | /// @dev Unlike {Gaussian.cdf}, this function doesn't assume any bounds on 43 | /// `_x`. Note that the function returns 0 early if |`_x`| is greater than 44 | /// 5.342 because the result is accurate within 2e-8. The result from this 45 | /// function is halved when computing the CDF value, so 2e-8 is sufficient 46 | /// for the desired 1e-8 precision in the CDF. 47 | /// @param _x The value to evaluate the erfc at, as an 18-decimal 48 | /// fixed-point number scaled to 2**96 base. 49 | /// @return r The erfc value at `_x`, as an 18-decimal fixed-point number. 50 | function erfc(int256 _x) internal pure returns (uint256 r) { 51 | int256 x; 52 | assembly { 53 | // Compute the absolute value of `_x` (credit to [Solady](https://github.com/Vectorized/solady/blob/2cead9a403ef788b5467e16fc5a166497be4f820/src/utils/FixedPointMathLib.sol#L935) 54 | // for the `abs` implementation). 55 | x := xor(sar(255, _x), add(sar(255, _x), _x)) 56 | } 57 | 58 | // If `x` is greater than 5.342, return 0 because the absolute error of 59 | // the rational approximation in the domain [0, 5.342] is ~1.343e-8`, 60 | // which is less than the necessary 2e-8. 61 | // `floor(5.342e18 / 2**96) = 0x5578d4fdf27cb98715bb60000`. 62 | if (x > 0x5578d4fdf27cb98715bb60000) { 63 | return 0; 64 | } 65 | 66 | // Compute the value using a (14, 5)-term rational approximation. 67 | // The polynomials `p` and `q` are evaluated using Horner's method. 68 | unchecked { 69 | // The first iteration of Horner's method is removed by scaling each 70 | // of the coefficients by `floor(2**96 / a_{n}) = 350643517`, where 71 | // `a_{n}` is the highest degree term's coefficient and scaling the 72 | // result back down at the end of the computation. Note that 73 | // `log(350643517) ≈ 8.54 > 8`, so we retain the desired 2e-8 74 | // precision. 75 | int256 p = 0x53f03f07e018b5e437ebc00000 - x; 76 | p = ((p * x) >> 96) - 0x9426c0da0db0196f4c940000000; 77 | p = ((p * x) >> 96) + 0x87ce3464514f9f2a96a900000000; 78 | p = ((p * x) >> 96) - 0x4aa878b19f5e1da7849da00000000; 79 | p = ((p * x) >> 96) + 0x19c6271833e59e7293644000000000; 80 | p = ((p * x) >> 96) - 0x57250cb5cfe1a6b98e584000000000; 81 | p = ((p * x) >> 96) + 0x986c8bb677c010a3ace48000000000; 82 | p = ((p * x) >> 96) - 0x13d63a9a5da47428a1670000000000; 83 | p = ((p * x) >> 96) - 0xf4d1deada9d68c8918f10000000000; 84 | p = ((p * x) >> 96) - 0x379fa110f89b0b537e2e20000000000; 85 | p = ((p * x) >> 96) + 0x14001c78d75852359284580000000000; 86 | p = ((p * x) >> 96) - 0x220176c16cc7b9b6d30dd00000000000; 87 | p = (p * x) + 0x14e66541b58fa8fa0dc2600000000000000000000000000000000000; 88 | 89 | // Unlike `p`, `q` can't be made monic because the scaling factor 90 | // results in too much precision loss: `log(2**96 / a_{n}) ≈ 1.52`. 91 | int256 q = 0x7bfa42098491b8000000000; 92 | q = ((q * x) >> 96) - 0x18e69e267814200000000000; 93 | q = ((q * x) >> 96) + 0x64ead0991554bc0000000000; 94 | q = ((q * x) >> 96) - 0x7faa085414fe440000000000; 95 | q = ((q * x) >> 96) + 0x1000000000000000000000000; 96 | 97 | assembly { 98 | // Convert the result back to 1e18 base and multiply by the 99 | // inverse of the scaling factor used to make `p` monic. 100 | // `floor(350643517 * 2**96 / 1e18) = 0x18189603334553000`. 101 | r := div(sdiv(p, q), 0x18189603334553000) 102 | 103 | // Subtract from 2 if `_x` is positive; 104 | // `2e18 = 0x1bc16d674ec80000`. 105 | if sgt(_x, 0) { r := sub(0x1bc16d674ec80000, r) } 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/GaussianYul.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | library GaussianYul { 5 | /// @notice Computes the cumulative distribution function (CDF) for an 6 | /// 18-decimal fixed-point numbers `_x`, `_mu`, and `_sigma` with 1e-8 7 | /// precision. 8 | /// @dev The function doesn't check for overflows and underflows and assumes 9 | /// the following bounds: `_x` ∈ [-1e23, 1e23], `_mu` ∈ [-1e20, 1e20], 10 | /// `_sigma` ∈ (0, 1e19]. 11 | /// @param _x The value to evaluate the CDF at, as an 18-decimal fixed-point 12 | /// number. 13 | /// @param _mu The mean of the distribution, as an 18-decimal fixed-point 14 | /// number. 15 | /// @param _sigma The standard deviation of the distribution, as an 16 | /// 18-decimal fixed-point number. 17 | /// @return r The CDF value at `_x`, `_mu`, and `_sigma`, as an 18-decimal 18 | /// fixed-point number. 19 | function cdf(int256 _x, int256 _mu, uint256 _sigma) internal pure returns (uint256 r) { 20 | assembly { 21 | // Computes the complentary error function (erfc) for an 18-decimal 22 | // fixed-point number `_a` scaled to 2**96 base with 2e-8 precision. 23 | // Unlike {GaussianYul.cdf}, this function doesn't assume any bounds 24 | // on `_a`. Note that the function returns 0 early if |`_a`| is 25 | // greater than 5.342 because the result is accurate within 2e-8. 26 | // The result from this function is halved when computing the CDF 27 | // value, so 2e-8 is sufficient for the desired 1e-8 precision in 28 | // the CDF. 29 | function erfc(_a) -> b { 30 | // Compute the absolute value of `_a` (credit to [Solady](https://github.com/Vectorized/solady/blob/2cead9a403ef788b5467e16fc5a166497be4f820/src/utils/FixedPointMathLib.sol#L935) 31 | // for the `abs` implementation). 32 | let a := xor(sar(255, _a), add(sar(255, _a), _a)) 33 | 34 | // If `a` is less than to 5.342, compute the value using a 35 | // (14, 5)-term rational approximation and Horner's method for 36 | // the polynomials `p` and `q`. Otherwise, skip the 37 | // approximation and return 0 because the absolute error of the 38 | // approximation in the domain [0, 5.342] is ~1.343e-8`, which 39 | // is less than the necessary 2e-8. 40 | // `floor(5.342e18 / 2**96) = 0x5578d4fdf27cb98715bb60000`. 41 | if lt(a, 0x5578d4fdf27cb98715bb60000) { 42 | // The first iteration of Horner's method is removed by 43 | // scaling each of the coefficients by 44 | // `floor(2**96 / a_{n}) = 350643517`, where `a_{n}` is the 45 | // highest degree term's coefficient and scaling the result 46 | // back down at the end of the computation. Note that 47 | // `log(350643517) ≈ 8.54 > 8`, so we retain the desired 48 | // 2e-8 precision. 49 | let p := sub(0x53f03f07e018b5e437ebc00000, a) 50 | p := sub(sar(96, mul(p, a)), 0x9426c0da0db0196f4c940000000) 51 | p := add(sar(96, mul(p, a)), 0x87ce3464514f9f2a96a900000000) 52 | p := sub(sar(96, mul(p, a)), 0x4aa878b19f5e1da7849da00000000) 53 | p := add(sar(96, mul(p, a)), 0x19c6271833e59e7293644000000000) 54 | p := sub(sar(96, mul(p, a)), 0x57250cb5cfe1a6b98e584000000000) 55 | p := add(sar(96, mul(p, a)), 0x986c8bb677c010a3ace48000000000) 56 | p := sub(sar(96, mul(p, a)), 0x13d63a9a5da47428a1670000000000) 57 | p := sub(sar(96, mul(p, a)), 0xf4d1deada9d68c8918f10000000000) 58 | p := sub(sar(96, mul(p, a)), 0x379fa110f89b0b537e2e20000000000) 59 | p := add(sar(96, mul(p, a)), 0x14001c78d75852359284580000000000) 60 | p := sub(sar(96, mul(p, a)), 0x220176c16cc7b9b6d30dd00000000000) 61 | p := add(mul(p, a), 0x14e66541b58fa8fa0dc2600000000000000000000000000000000000) 62 | 63 | // Unlike `p`, `q` can't be made monic because the scaling 64 | // factor results in too much precision loss: 65 | // `log(2**96 / a_{n}) ≈ 1.52`. 66 | let q := 0x7bfa42098491b8000000000 67 | q := sub(sar(96, mul(q, a)), 0x18e69e267814200000000000) 68 | q := add(sar(96, mul(q, a)), 0x64ead0991554bc0000000000) 69 | q := sub(sar(96, mul(q, a)), 0x7faa085414fe440000000000) 70 | q := add(sar(96, mul(q, a)), 0x1000000000000000000000000) 71 | 72 | // Convert the result back to 1e18 base and multiply by the 73 | // inverse of the scaling factor used to make `p` monic. 74 | // `floor(350643517 * 2**96 / 1e18) = 0x18189603334553000`. 75 | b := div(sdiv(p, q), 0x18189603334553000) 76 | 77 | // Subtract from 2 if `_x` is positive; 78 | // `2e18 = 0x1bc16d674ec80000`. 79 | if sgt(_a, 0) { b := sub(0x1bc16d674ec80000, b) } 80 | } 81 | } 82 | 83 | // Compute `(_x - _mu) / (_sigma * sqrt(2))` and scale it to 2**96 84 | // base. 85 | // ``` 86 | // (2**96 / WAD) * ((_x - _mu) * WAD / (_sigma * sqrt(2) * WAD / WAD)) 87 | // = (_x - _mu) * 2**96 / (_sigma * sqrt(2)) 88 | // = (_x - _mu) * 0xb504f333f9de6484597d89b3 / _sigma. 89 | // ``` 90 | let x := sdiv(mul(sub(_x, _mu), 0xb504f333f9de6484597d89b3), _sigma) 91 | 92 | // Divide result by 2. 93 | r := shr(1, erfc(x)) 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /test/Gaussian.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import { Test } from "forge-std/Test.sol"; 5 | 6 | import { Gaussian } from "src/Gaussian.sol"; 7 | import { GaussianYul } from "src/GaussianYul.sol"; 8 | 9 | contract GaussianTest is Test { 10 | // ------------------------------------------------------------------------- 11 | // {Gaussian} 12 | // ------------------------------------------------------------------------- 13 | 14 | /// @notice Tests the gas cost of {Gaussian.cdf} with hard-coded parameters. 15 | function testGas_cdf() public pure { 16 | Gaussian.cdf({ _x: 1e18, _mu: 0, _sigma: 1e18 }); 17 | } 18 | 19 | /// @notice Tests the gas cost of {Gaussian.cdf} with fuzzed inputs with the 20 | /// following bounds: `_x` ∈ [-1e23, 1e23], `_mu` ∈ [-1e20, 1e20], `_sigma` 21 | /// ∈ (0, 1e19]. 22 | /// @param _x The value to evaluate the CDF at, as an 18-decimal fixed-point 23 | /// number. 24 | /// @param _mu The mean of the distribution, as an 18-decimal fixed-point 25 | /// number. 26 | /// @param _sigma The standard deviation of the distribution, as an 27 | /// 18-decimal fixed-point number. 28 | function testGas_cdf_fuzz(int256 _x, int256 _mu, uint256 _sigma) public pure { 29 | _x = bound(_x, -1e23, 1e23); 30 | _mu = bound(_mu, -1e20, 1e20); 31 | _sigma = bound(_sigma, 1, 1e19); 32 | Gaussian.cdf({ _x: _x, _mu: _mu, _sigma: _sigma }); 33 | } 34 | 35 | // ------------------------------------------------------------------------- 36 | // {GaussianYul} 37 | // ------------------------------------------------------------------------- 38 | 39 | /// @notice Tests the gas cost of {GaussianYul.cdf} with hard-coded 40 | /// parameters. 41 | function testGas_cdf_yul() public pure { 42 | GaussianYul.cdf({ _x: 1e18, _mu: 0, _sigma: 1e18 }); 43 | } 44 | 45 | /// @notice Tests the gas cost of {GaussianYul.cdf} with fuzzed inputs with 46 | /// the following bounds: `_x` ∈ [-1e23, 1e23], `_mu` ∈ [-1e20, 1e20], 47 | /// `_sigma` ∈ (0, 1e19]. 48 | /// @param _x The value to evaluate the CDF at, as an 18-decimal fixed-point 49 | /// number. 50 | /// @param _mu The mean of the distribution, as an 18-decimal fixed-point 51 | /// number. 52 | /// @param _sigma The standard deviation of the distribution, as an 53 | /// 18-decimal fixed-point number. 54 | function testGas_cdf_yul_fuzz(int256 _x, int256 _mu, uint256 _sigma) public pure { 55 | _x = bound(_x, -1e23, 1e23); 56 | _mu = bound(_mu, -1e20, 1e20); 57 | _sigma = bound(_sigma, 1, 1e19); 58 | GaussianYul.cdf({ _x: _x, _mu: _mu, _sigma: _sigma }); 59 | } 60 | } 61 | --------------------------------------------------------------------------------