├── .editorconfig ├── .git-blame-ignore-revs ├── .github └── workflows │ ├── main.yaml │ └── publish.yaml ├── .gitignore ├── .gitmodules ├── .rustfmt.toml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE.txt ├── README.md ├── ci ├── bench-icount.sh ├── ci-util.py ├── docker │ ├── aarch64-unknown-linux-gnu │ │ └── Dockerfile │ ├── arm-unknown-linux-gnueabi │ │ └── Dockerfile │ ├── arm-unknown-linux-gnueabihf │ │ └── Dockerfile │ ├── armv7-unknown-linux-gnueabihf │ │ └── Dockerfile │ ├── i586-unknown-linux-gnu │ │ └── Dockerfile │ ├── i686-unknown-linux-gnu │ │ └── Dockerfile │ ├── loongarch64-unknown-linux-gnu │ │ └── Dockerfile │ ├── mips-unknown-linux-gnu │ │ └── Dockerfile │ ├── mips64-unknown-linux-gnuabi64 │ │ └── Dockerfile │ ├── mips64el-unknown-linux-gnuabi64 │ │ └── Dockerfile │ ├── mipsel-unknown-linux-gnu │ │ └── Dockerfile │ ├── powerpc-unknown-linux-gnu │ │ └── Dockerfile │ ├── powerpc64-unknown-linux-gnu │ │ └── Dockerfile │ ├── powerpc64le-unknown-linux-gnu │ │ └── Dockerfile │ ├── riscv64gc-unknown-linux-gnu │ │ └── Dockerfile │ ├── thumbv6m-none-eabi │ │ └── Dockerfile │ ├── thumbv7em-none-eabi │ │ └── Dockerfile │ ├── thumbv7em-none-eabihf │ │ └── Dockerfile │ ├── thumbv7m-none-eabi │ │ └── Dockerfile │ └── x86_64-unknown-linux-gnu │ │ └── Dockerfile ├── run-docker.sh └── run.sh ├── crates ├── compiler-builtins-smoke-test │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ ├── lib.rs │ │ └── math.rs ├── libm-macros │ ├── Cargo.toml │ ├── src │ │ ├── enums.rs │ │ ├── lib.rs │ │ ├── parse.rs │ │ └── shared.rs │ └── tests │ │ ├── basic.rs │ │ └── enum.rs ├── libm-test │ ├── Cargo.toml │ ├── benches │ │ ├── icount.rs │ │ └── random.rs │ ├── build.rs │ ├── examples │ │ ├── plot_domains.rs │ │ └── plot_file.jl │ ├── src │ │ ├── domain.rs │ │ ├── f8_impl.rs │ │ ├── generate.rs │ │ ├── generate │ │ │ ├── case_list.rs │ │ │ ├── edge_cases.rs │ │ │ ├── random.rs │ │ │ └── spaced.rs │ │ ├── lib.rs │ │ ├── mpfloat.rs │ │ ├── num.rs │ │ ├── op.rs │ │ ├── precision.rs │ │ ├── run_cfg.rs │ │ └── test_traits.rs │ └── tests │ │ ├── check_coverage.rs │ │ ├── compare_built_musl.rs │ │ ├── multiprecision.rs │ │ ├── standalone.rs │ │ ├── u256.rs │ │ └── z_extensive │ │ ├── main.rs │ │ └── run.rs ├── musl-math-sys │ ├── Cargo.toml │ ├── build.rs │ ├── c_patches │ │ ├── alias.c │ │ └── features.h │ └── src │ │ └── lib.rs └── util │ ├── Cargo.toml │ ├── build.rs │ └── src │ └── main.rs ├── etc ├── function-definitions.json ├── function-list.txt └── update-api-list.py └── libm ├── Cargo.toml ├── LICENSE.txt ├── README.md ├── build.rs ├── configure.rs └── src ├── lib.rs ├── libm_helper.rs └── math ├── acos.rs ├── acosf.rs ├── acosh.rs ├── acoshf.rs ├── arch ├── aarch64.rs ├── i586.rs ├── i686.rs ├── mod.rs └── wasm32.rs ├── asin.rs ├── asinf.rs ├── asinh.rs ├── asinhf.rs ├── atan.rs ├── atan2.rs ├── atan2f.rs ├── atanf.rs ├── atanh.rs ├── atanhf.rs ├── cbrt.rs ├── cbrtf.rs ├── ceil.rs ├── copysign.rs ├── copysignf.rs ├── copysignf128.rs ├── copysignf16.rs ├── cos.rs ├── cosf.rs ├── cosh.rs ├── coshf.rs ├── erf.rs ├── erff.rs ├── exp.rs ├── exp10.rs ├── exp10f.rs ├── exp2.rs ├── exp2f.rs ├── expf.rs ├── expm1.rs ├── expm1f.rs ├── expo2.rs ├── fabs.rs ├── fabsf.rs ├── fabsf128.rs ├── fabsf16.rs ├── fdim.rs ├── fdimf.rs ├── fdimf128.rs ├── fdimf16.rs ├── floor.rs ├── floorf.rs ├── floorf128.rs ├── floorf16.rs ├── fma.rs ├── fma_wide.rs ├── fmin_fmax.rs ├── fminimum_fmaximum.rs ├── fminimum_fmaximum_num.rs ├── fmod.rs ├── fmodf.rs ├── fmodf128.rs ├── fmodf16.rs ├── frexp.rs ├── frexpf.rs ├── generic ├── ceil.rs ├── copysign.rs ├── fabs.rs ├── fdim.rs ├── floor.rs ├── fmax.rs ├── fmaximum.rs ├── fmaximum_num.rs ├── fmin.rs ├── fminimum.rs ├── fminimum_num.rs ├── fmod.rs ├── mod.rs ├── rint.rs ├── round.rs ├── scalbn.rs ├── sqrt.rs └── trunc.rs ├── hypot.rs ├── hypotf.rs ├── ilogb.rs ├── ilogbf.rs ├── j0.rs ├── j0f.rs ├── j1.rs ├── j1f.rs ├── jn.rs ├── jnf.rs ├── k_cos.rs ├── k_cosf.rs ├── k_expo2.rs ├── k_expo2f.rs ├── k_sin.rs ├── k_sinf.rs ├── k_tan.rs ├── k_tanf.rs ├── ldexp.rs ├── ldexpf.rs ├── ldexpf128.rs ├── ldexpf16.rs ├── lgamma.rs ├── lgamma_r.rs ├── lgammaf.rs ├── lgammaf_r.rs ├── log.rs ├── log10.rs ├── log10f.rs ├── log1p.rs ├── log1pf.rs ├── log2.rs ├── log2f.rs ├── logf.rs ├── mod.rs ├── modf.rs ├── modff.rs ├── nextafter.rs ├── nextafterf.rs ├── pow.rs ├── powf.rs ├── rem_pio2.rs ├── rem_pio2_large.rs ├── rem_pio2f.rs ├── remainder.rs ├── remainderf.rs ├── remquo.rs ├── remquof.rs ├── rint.rs ├── round.rs ├── roundeven.rs ├── roundf.rs ├── roundf128.rs ├── roundf16.rs ├── scalbn.rs ├── scalbnf.rs ├── scalbnf128.rs ├── scalbnf16.rs ├── sin.rs ├── sincos.rs ├── sincosf.rs ├── sinf.rs ├── sinh.rs ├── sinhf.rs ├── sqrt.rs ├── sqrtf.rs ├── sqrtf128.rs ├── sqrtf16.rs ├── support ├── big.rs ├── big │ └── tests.rs ├── env.rs ├── float_traits.rs ├── hex_float.rs ├── int_traits.rs ├── macros.rs └── mod.rs ├── tan.rs ├── tanf.rs ├── tanh.rs ├── tanhf.rs ├── tgamma.rs ├── tgammaf.rs ├── trunc.rs ├── truncf.rs ├── truncf128.rs └── truncf16.rs /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [*.md] 16 | # double whitespace at end of line 17 | # denotes a line break in Markdown 18 | trim_trailing_whitespace = false 19 | 20 | [*.yml] 21 | indent_size = 2 22 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Use `git config blame.ignorerevsfile .git-blame-ignore-revs` to make 2 | # `git blame` ignore the following commits. 3 | 4 | # Reformat with a new `.rustfmt.toml` 5 | 5882cabb83c30bf7c36023f9a55a80583636b0e8 6 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: Release-plz 2 | 3 | permissions: 4 | pull-requests: write 5 | contents: write 6 | 7 | on: 8 | push: 9 | branches: 10 | - master 11 | 12 | jobs: 13 | release-plz: 14 | name: Release-plz 15 | runs-on: ubuntu-24.04 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | - name: Install Rust (rustup) 22 | run: rustup update nightly --no-self-update && rustup default nightly 23 | - name: Run release-plz 24 | uses: MarcoIeni/release-plz-action@v0.5 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **.bk 2 | .#* 3 | /bin 4 | /math/src 5 | target 6 | Cargo.lock 7 | **.tar.gz 8 | 9 | # Benchmark cache 10 | iai-home 11 | baseline-* 12 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "musl"] 2 | path = crates/musl-math-sys/musl 3 | url = https://git.musl-libc.org/git/musl 4 | shallow = true 5 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | # This matches rustc 2 | style_edition = "2024" 3 | use_small_heuristics = "Max" 4 | group_imports = "StdExternalCrate" 5 | imports_granularity = "Module" 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | - Pick your favorite math function from the [issue tracker]. 4 | - Look for the C implementation of the function in the [MUSL source code][src]. 5 | - Copy paste the C code into a Rust file in the `src/math` directory and adjust 6 | `src/math/mod.rs` accordingly. Also, uncomment the corresponding trait method 7 | in `src/lib.rs`. 8 | - Write some simple tests in your module (using `#[test]`) 9 | - Run `cargo test` to make sure it works. Full tests are only run when enabling 10 | features, see [Testing](#testing) below. 11 | - Send us a pull request! Make sure to run `cargo fmt` on your code before 12 | sending the PR. Also include "closes #42" in the PR description to close the 13 | corresponding issue. 14 | - :tada: 15 | 16 | [issue tracker]: https://github.com/rust-lang/libm/issues 17 | [src]: https://git.musl-libc.org/cgit/musl/tree/src/math 18 | [`src/math/truncf.rs`]: https://github.com/rust-lang/libm/blob/master/src/math/truncf.rs 19 | 20 | Check [PR #65] for an example. 21 | 22 | [PR #65]: https://github.com/rust-lang/libm/pull/65 23 | 24 | ## Tips and tricks 25 | 26 | - *IMPORTANT* The code in this crate will end up being used in the `core` crate so it can **not** 27 | have any external dependencies (other than `core` itself). 28 | 29 | - Only use relative imports within the `math` directory / module, e.g. `use self::fabs::fabs` or 30 | `use super::k_cos`. Absolute imports from core are OK, e.g. `use core::u64`. 31 | 32 | - To reinterpret a float as an integer use the `to_bits` method. The MUSL code uses the 33 | `GET_FLOAT_WORD` macro, or a union, to do this operation. 34 | 35 | - To reinterpret an integer as a float use the `f32::from_bits` constructor. The MUSL code uses the 36 | `SET_FLOAT_WORD` macro, or a union, to do this operation. 37 | 38 | - You may use other methods from core like `f64::is_nan`, etc. as appropriate. 39 | 40 | - If you're implementing one of the private double-underscore functions, take a look at the 41 | "source" name in the comment at the top for an idea for alternate naming. For example, `__sin` 42 | was renamed to `k_sin` after the FreeBSD source code naming. Do `use` these private functions in 43 | `mod.rs`. 44 | 45 | - You may encounter weird literals like `0x1p127f` in the MUSL code. These are hexadecimal floating 46 | point literals. Rust (the language) doesn't support these kind of literals. This crate provides 47 | two macros, `hf32!` and `hf64!`, which convert string literals to floats at compile time. 48 | 49 | ```rust 50 | assert_eq!(hf32!("0x1.ffep+8").to_bits(), 0x43fff000); 51 | assert_eq!(hf64!("0x1.ffep+8").to_bits(), 0x407ffe0000000000); 52 | ``` 53 | 54 | - Rust code panics on arithmetic overflows when not optimized. You may need to use the [`Wrapping`] 55 | newtype to avoid this problem, or individual methods like [`wrapping_add`]. 56 | 57 | [`Wrapping`]: https://doc.rust-lang.org/std/num/struct.Wrapping.html 58 | [`wrapping_add`]: https://doc.rust-lang.org/std/primitive.u32.html#method.wrapping_add 59 | 60 | ## Testing 61 | 62 | Normal tests can be executed with: 63 | 64 | ```sh 65 | # Tests against musl require that the submodule is up to date. 66 | git submodule init 67 | git submodule update 68 | 69 | # `--release` ables more test cases 70 | cargo test --release 71 | ``` 72 | 73 | If you are on a system that cannot build musl or MPFR, passing 74 | `--no-default-features` will run some limited tests. 75 | 76 | The multiprecision tests use the [`rug`] crate for bindings to MPFR. MPFR can 77 | be difficult to build on non-Unix systems, refer to [`gmp_mpfr_sys`] for help. 78 | 79 | `build-musl` does not build with MSVC, Wasm, or Thumb. 80 | 81 | [`rug`]: https://docs.rs/rug/latest/rug/ 82 | [`gmp_mpfr_sys`]: https://docs.rs/gmp-mpfr-sys/1.6.4/gmp_mpfr_sys/ 83 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "libm", 5 | "crates/libm-macros", 6 | "crates/libm-test", 7 | "crates/musl-math-sys", 8 | "crates/util", 9 | ] 10 | default-members = [ 11 | "libm", 12 | "crates/libm-macros", 13 | "crates/libm-test" 14 | ] 15 | exclude = [ 16 | # Requires `panic = abort` so can't be a member of the workspace 17 | "crates/compiler-builtins-smoke-test", 18 | ] 19 | 20 | # The default release profile is unchanged. 21 | 22 | # Release mode with debug assertions 23 | [profile.release-checked] 24 | inherits = "release" 25 | debug-assertions = true 26 | overflow-checks = true 27 | 28 | # Release with maximum optimizations, which is very slow to build. This is also 29 | # what is needed to check `no-panic`. 30 | [profile.release-opt] 31 | inherits = "release" 32 | codegen-units = 1 33 | lto = "fat" 34 | 35 | [profile.bench] 36 | # Required for iai-callgrind 37 | debug = true 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `libm` 2 | 3 | A port of [MUSL]'s libm to Rust. 4 | 5 | > [!NOTE] 6 | > The `libm` crate has been merged into the `compiler-builtins` repository. Future 7 | > development work will take place there: https://github.com/rust-lang/compiler-builtins. 8 | 9 | [MUSL]: https://musl.libc.org/ 10 | 11 | ## Goals 12 | 13 | The short term goal of this library is to [enable math support (e.g. `sin`, `atan2`) for the 14 | `wasm32-unknown-unknown` target][wasm] (cf. [rust-lang/compiler-builtins][pr]). The longer 15 | term goal is to enable [math support in the `core` crate][core]. 16 | 17 | [wasm]: https://github.com/rust-lang/libm/milestone/1 18 | [pr]: https://github.com/rust-lang/compiler-builtins/pull/248 19 | [core]: https://github.com/rust-lang/libm/milestone/2 20 | 21 | ## Already usable 22 | 23 | This crate is [on crates.io] and can be used today in stable `#![no_std]` programs. 24 | 25 | The API documentation can be found [here](https://docs.rs/libm). 26 | 27 | [on crates.io]: https://crates.io/crates/libm 28 | 29 | ## Benchmark 30 | [benchmark]: #benchmark 31 | 32 | The benchmarks are located in `crates/libm-bench` and require a nightly Rust toolchain. 33 | To run all benchmarks: 34 | 35 | > cargo +nightly bench --all 36 | 37 | ## Contributing 38 | 39 | Please check [CONTRIBUTING.md](CONTRIBUTING.md) 40 | 41 | ## Minimum Rust version policy 42 | 43 | This crate supports rustc 1.63 and newer. 44 | 45 | ## License 46 | 47 | Usage is licensed under the MIT license ([LICENSE-MIT](LICENSE-MIT) or 48 | https://opensource.org/licenses/MIT). 49 | 50 | 51 | ### Contribution 52 | 53 | Contributions are licensed under both the MIT license and the Apache License, 54 | Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 55 | https://www.apache.org/licenses/LICENSE-2.0). Unless you explicitly state 56 | otherwise, any contribution intentionally submitted for inclusion in the work 57 | by you, as defined in the Apache-2.0 license, shall be dual licensed as 58 | mentioned, without any additional terms or conditions. 59 | 60 | See `LICENSE.txt` for full details. 61 | -------------------------------------------------------------------------------- /ci/bench-icount.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eux 4 | 5 | iai_home="iai-home" 6 | 7 | # Download the baseline from master 8 | ./ci/ci-util.py locate-baseline --download --extract 9 | 10 | # Run benchmarks once 11 | function run_icount_benchmarks() { 12 | cargo_args=( 13 | "--bench" "icount" 14 | "--no-default-features" 15 | "--features" "unstable,unstable-float,icount" 16 | ) 17 | 18 | iai_args=( 19 | "--home" "$(pwd)/$iai_home" 20 | "--regression=ir=5.0" 21 | "--save-summary" 22 | ) 23 | 24 | # Parse `cargo_arg0 cargo_arg1 -- iai_arg0 iai_arg1` syntax 25 | parsing_iai_args=0 26 | while [ "$#" -gt 0 ]; do 27 | if [ "$parsing_iai_args" == "1" ]; then 28 | iai_args+=("$1") 29 | elif [ "$1" == "--" ]; then 30 | parsing_iai_args=1 31 | else 32 | cargo_args+=("$1") 33 | fi 34 | 35 | shift 36 | done 37 | 38 | # Run iai-callgrind benchmarks 39 | cargo bench "${cargo_args[@]}" -- "${iai_args[@]}" 40 | 41 | # NB: iai-callgrind should exit on error but does not, so we inspect the sumary 42 | # for errors. See https://github.com/iai-callgrind/iai-callgrind/issues/337 43 | if [ -n "${PR_NUMBER:-}" ]; then 44 | # If this is for a pull request, ignore regressions if specified. 45 | ./ci/ci-util.py check-regressions --home "$iai_home" --allow-pr-override "$PR_NUMBER" 46 | else 47 | ./ci/ci-util.py check-regressions --home "$iai_home" || true 48 | fi 49 | } 50 | 51 | # Run once with softfloats, once with arch instructions enabled 52 | run_icount_benchmarks --features force-soft-floats -- --save-baseline=softfloat 53 | run_icount_benchmarks -- --save-baseline=hardfloat 54 | 55 | # Name and tar the new baseline 56 | name="baseline-icount-$(date -u +'%Y%m%d%H%M')-${GITHUB_SHA:0:12}" 57 | echo "BASELINE_NAME=$name" >>"$GITHUB_ENV" 58 | tar cJf "$name.tar.xz" "$iai_home" 59 | -------------------------------------------------------------------------------- /ci/docker/aarch64-unknown-linux-gnu/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y --no-install-recommends \ 5 | gcc libc6-dev ca-certificates \ 6 | gcc-aarch64-linux-gnu m4 make libc6-dev-arm64-cross \ 7 | qemu-user-static 8 | 9 | ENV TOOLCHAIN_PREFIX=aarch64-linux-gnu- 10 | ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ 11 | CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER=qemu-aarch64-static \ 12 | AR_aarch64_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ 13 | CC_aarch64_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ 14 | QEMU_LD_PREFIX=/usr/aarch64-linux-gnu \ 15 | RUST_TEST_THREADS=1 16 | -------------------------------------------------------------------------------- /ci/docker/arm-unknown-linux-gnueabi/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y --no-install-recommends \ 5 | gcc libc6-dev ca-certificates \ 6 | gcc-arm-linux-gnueabi libc6-dev-armel-cross qemu-user-static 7 | 8 | ENV TOOLCHAIN_PREFIX=arm-linux-gnueabi- 9 | ENV CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABI_LINKER="$TOOLCHAIN_PREFIX"gcc \ 10 | CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABI_RUNNER=qemu-arm-static \ 11 | AR_arm_unknown_linux_gnueabi="$TOOLCHAIN_PREFIX"ar \ 12 | CC_arm_unknown_linux_gnueabi="$TOOLCHAIN_PREFIX"gcc \ 13 | QEMU_LD_PREFIX=/usr/arm-linux-gnueabi \ 14 | RUST_TEST_THREADS=1 15 | -------------------------------------------------------------------------------- /ci/docker/arm-unknown-linux-gnueabihf/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y --no-install-recommends \ 5 | gcc libc6-dev ca-certificates \ 6 | gcc-arm-linux-gnueabihf libc6-dev-armhf-cross qemu-user-static 7 | 8 | ENV TOOLCHAIN_PREFIX=arm-linux-gnueabihf- 9 | ENV CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER="$TOOLCHAIN_PREFIX"gcc \ 10 | CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_RUNNER=qemu-arm-static \ 11 | AR_arm_unknown_linux_gnueabihf="$TOOLCHAIN_PREFIX"ar \ 12 | CC_arm_unknown_linux_gnueabihf="$TOOLCHAIN_PREFIX"gcc \ 13 | QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf \ 14 | RUST_TEST_THREADS=1 15 | -------------------------------------------------------------------------------- /ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y --no-install-recommends \ 5 | gcc libc6-dev ca-certificates \ 6 | gcc-arm-linux-gnueabihf libc6-dev-armhf-cross qemu-user-static 7 | 8 | ENV TOOLCHAIN_PREFIX=arm-linux-gnueabihf- 9 | ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER="$TOOLCHAIN_PREFIX"gcc \ 10 | CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUNNER=qemu-arm-static \ 11 | AR_armv7_unknown_linux_gnueabihf="$TOOLCHAIN_PREFIX"ar \ 12 | CC_armv7_unknown_linux_gnueabihf="$TOOLCHAIN_PREFIX"gcc \ 13 | QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf \ 14 | RUST_TEST_THREADS=1 15 | -------------------------------------------------------------------------------- /ci/docker/i586-unknown-linux-gnu/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y --no-install-recommends \ 5 | gcc-multilib m4 make libc6-dev ca-certificates 6 | -------------------------------------------------------------------------------- /ci/docker/i686-unknown-linux-gnu/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y --no-install-recommends \ 5 | gcc-multilib m4 make libc6-dev ca-certificates 6 | -------------------------------------------------------------------------------- /ci/docker/loongarch64-unknown-linux-gnu/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y --no-install-recommends \ 5 | gcc libc6-dev qemu-user-static ca-certificates \ 6 | gcc-14-loongarch64-linux-gnu libc6-dev-loong64-cross 7 | 8 | ENV CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_LINKER=loongarch64-linux-gnu-gcc-14 \ 9 | CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_RUNNER=qemu-loongarch64-static \ 10 | AR_loongarch64_unknown_linux_gnu=loongarch64-linux-gnu-ar \ 11 | CC_loongarch64_unknown_linux_gnu=loongarch64-linux-gnu-gcc-14 \ 12 | QEMU_LD_PREFIX=/usr/loongarch64-linux-gnu \ 13 | RUST_TEST_THREADS=1 14 | -------------------------------------------------------------------------------- /ci/docker/mips-unknown-linux-gnu/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y --no-install-recommends \ 5 | gcc libc6-dev ca-certificates \ 6 | gcc-mips-linux-gnu libc6-dev-mips-cross \ 7 | binfmt-support qemu-user-static qemu-system-mips 8 | 9 | ENV TOOLCHAIN_PREFIX=mips-linux-gnu- 10 | ENV CARGO_TARGET_MIPS_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ 11 | CARGO_TARGET_MIPS_UNKNOWN_LINUX_GNU_RUNNER=qemu-mips-static \ 12 | AR_mips_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ 13 | CC_mips_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ 14 | QEMU_LD_PREFIX=/usr/mips-linux-gnu \ 15 | RUST_TEST_THREADS=1 16 | -------------------------------------------------------------------------------- /ci/docker/mips64-unknown-linux-gnuabi64/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y --no-install-recommends \ 5 | ca-certificates \ 6 | gcc \ 7 | gcc-mips64-linux-gnuabi64 \ 8 | libc6-dev \ 9 | libc6-dev-mips64-cross \ 10 | qemu-user-static \ 11 | qemu-system-mips 12 | 13 | ENV TOOLCHAIN_PREFIX=mips64-linux-gnuabi64- 14 | ENV CARGO_TARGET_MIPS64_UNKNOWN_LINUX_GNUABI64_LINKER="$TOOLCHAIN_PREFIX"gcc \ 15 | CARGO_TARGET_MIPS64_UNKNOWN_LINUX_GNUABI64_RUNNER=qemu-mips64-static \ 16 | AR_mips64_unknown_linux_gnuabi64="$TOOLCHAIN_PREFIX"ar \ 17 | CC_mips64_unknown_linux_gnuabi64="$TOOLCHAIN_PREFIX"gcc \ 18 | QEMU_LD_PREFIX=/usr/mips64-linux-gnuabi64 \ 19 | RUST_TEST_THREADS=1 20 | -------------------------------------------------------------------------------- /ci/docker/mips64el-unknown-linux-gnuabi64/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y --no-install-recommends \ 5 | ca-certificates \ 6 | gcc \ 7 | gcc-mips64el-linux-gnuabi64 \ 8 | libc6-dev \ 9 | libc6-dev-mips64el-cross \ 10 | qemu-user-static 11 | 12 | ENV TOOLCHAIN_PREFIX=mips64el-linux-gnuabi64- 13 | ENV CARGO_TARGET_MIPS64EL_UNKNOWN_LINUX_GNUABI64_LINKER="$TOOLCHAIN_PREFIX"gcc \ 14 | CARGO_TARGET_MIPS64EL_UNKNOWN_LINUX_GNUABI64_RUNNER=qemu-mips64el-static \ 15 | AR_mips64el_unknown_linux_gnuabi64="$TOOLCHAIN_PREFIX"ar \ 16 | CC_mips64el_unknown_linux_gnuabi64="$TOOLCHAIN_PREFIX"gcc \ 17 | QEMU_LD_PREFIX=/usr/mips64el-linux-gnuabi64 \ 18 | RUST_TEST_THREADS=1 19 | -------------------------------------------------------------------------------- /ci/docker/mipsel-unknown-linux-gnu/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y --no-install-recommends \ 5 | gcc libc6-dev ca-certificates \ 6 | gcc-mipsel-linux-gnu libc6-dev-mipsel-cross \ 7 | binfmt-support qemu-user-static 8 | 9 | ENV TOOLCHAIN_PREFIX=mipsel-linux-gnu- 10 | ENV CARGO_TARGET_MIPSEL_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ 11 | CARGO_TARGET_MIPSEL_UNKNOWN_LINUX_GNU_RUNNER=qemu-mipsel-static \ 12 | AR_mipsel_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ 13 | CC_mipsel_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ 14 | QEMU_LD_PREFIX=/usr/mipsel-linux-gnu \ 15 | RUST_TEST_THREADS=1 16 | -------------------------------------------------------------------------------- /ci/docker/powerpc-unknown-linux-gnu/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y --no-install-recommends \ 5 | gcc libc6-dev qemu-user-static ca-certificates \ 6 | gcc-powerpc-linux-gnu libc6-dev-powerpc-cross \ 7 | qemu-system-ppc 8 | 9 | ENV TOOLCHAIN_PREFIX=powerpc-linux-gnu- 10 | ENV CARGO_TARGET_POWERPC_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ 11 | CARGO_TARGET_POWERPC_UNKNOWN_LINUX_GNU_RUNNER=qemu-ppc-static \ 12 | AR_powerpc_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ 13 | CC_powerpc_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ 14 | QEMU_LD_PREFIX=/usr/powerpc-linux-gnu \ 15 | RUST_TEST_THREADS=1 16 | -------------------------------------------------------------------------------- /ci/docker/powerpc64-unknown-linux-gnu/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y --no-install-recommends \ 5 | gcc libc6-dev ca-certificates \ 6 | gcc-powerpc64-linux-gnu libc6-dev-ppc64-cross \ 7 | binfmt-support qemu-user-static qemu-system-ppc 8 | 9 | ENV TOOLCHAIN_PREFIX=powerpc64-linux-gnu- 10 | ENV CARGO_TARGET_POWERPC64_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ 11 | CARGO_TARGET_POWERPC64_UNKNOWN_LINUX_GNU_RUNNER=qemu-ppc64-static \ 12 | AR_powerpc64_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ 13 | CC_powerpc64_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ 14 | QEMU_LD_PREFIX=/usr/powerpc64-linux-gnu \ 15 | RUST_TEST_THREADS=1 16 | -------------------------------------------------------------------------------- /ci/docker/powerpc64le-unknown-linux-gnu/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y --no-install-recommends \ 5 | gcc libc6-dev qemu-user-static ca-certificates \ 6 | gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross \ 7 | qemu-system-ppc 8 | 9 | ENV TOOLCHAIN_PREFIX=powerpc64le-linux-gnu- 10 | ENV CARGO_TARGET_POWERPC64LE_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ 11 | CARGO_TARGET_POWERPC64LE_UNKNOWN_LINUX_GNU_RUNNER=qemu-ppc64le-static \ 12 | AR_powerpc64le_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ 13 | CC_powerpc64le_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ 14 | QEMU_CPU=POWER8 \ 15 | QEMU_LD_PREFIX=/usr/powerpc64le-linux-gnu \ 16 | RUST_TEST_THREADS=1 17 | -------------------------------------------------------------------------------- /ci/docker/riscv64gc-unknown-linux-gnu/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y --no-install-recommends \ 5 | gcc libc6-dev qemu-user-static ca-certificates \ 6 | gcc-riscv64-linux-gnu libc6-dev-riscv64-cross \ 7 | qemu-system-riscv64 8 | 9 | ENV TOOLCHAIN_PREFIX=riscv64-linux-gnu- 10 | ENV CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PREFIX"gcc \ 11 | CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_RUNNER=qemu-riscv64-static \ 12 | AR_riscv64gc_unknown_linux_gnu="$TOOLCHAIN_PREFIX"ar \ 13 | CC_riscv64gc_unknown_linux_gnu="$TOOLCHAIN_PREFIX"gcc \ 14 | QEMU_LD_PREFIX=/usr/riscv64-linux-gnu \ 15 | RUST_TEST_THREADS=1 16 | -------------------------------------------------------------------------------- /ci/docker/thumbv6m-none-eabi/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG IMAGE=ubuntu:24.04 2 | FROM $IMAGE 3 | 4 | RUN apt-get update && \ 5 | apt-get install -y --no-install-recommends \ 6 | gcc libc6-dev ca-certificates \ 7 | gcc-arm-none-eabi \ 8 | libnewlib-arm-none-eabi 9 | ENV BUILD_ONLY=1 10 | -------------------------------------------------------------------------------- /ci/docker/thumbv7em-none-eabi/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG IMAGE=ubuntu:24.04 2 | FROM $IMAGE 3 | 4 | RUN apt-get update && \ 5 | apt-get install -y --no-install-recommends \ 6 | gcc libc6-dev ca-certificates \ 7 | gcc-arm-none-eabi \ 8 | libnewlib-arm-none-eabi 9 | ENV BUILD_ONLY=1 10 | -------------------------------------------------------------------------------- /ci/docker/thumbv7em-none-eabihf/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG IMAGE=ubuntu:24.04 2 | FROM $IMAGE 3 | 4 | RUN apt-get update && \ 5 | apt-get install -y --no-install-recommends \ 6 | gcc libc6-dev ca-certificates \ 7 | gcc-arm-none-eabi \ 8 | libnewlib-arm-none-eabi 9 | ENV BUILD_ONLY=1 10 | -------------------------------------------------------------------------------- /ci/docker/thumbv7m-none-eabi/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG IMAGE=ubuntu:24.04 2 | FROM $IMAGE 3 | 4 | RUN apt-get update && \ 5 | apt-get install -y --no-install-recommends \ 6 | gcc libc6-dev ca-certificates \ 7 | gcc-arm-none-eabi \ 8 | libnewlib-arm-none-eabi 9 | ENV BUILD_ONLY=1 10 | -------------------------------------------------------------------------------- /ci/docker/x86_64-unknown-linux-gnu/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y --no-install-recommends \ 5 | gcc m4 make libc6-dev ca-certificates 6 | -------------------------------------------------------------------------------- /ci/run-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Small script to run tests for a target (or all targets) inside all the 4 | # respective docker images. 5 | 6 | set -euxo pipefail 7 | 8 | host_arch="$(uname -m | sed 's/arm64/aarch64/')" 9 | 10 | run() { 11 | local target=$1 12 | 13 | echo "testing target: $target" 14 | 15 | target_arch="$(echo "$target" | cut -d'-' -f1)" 16 | 17 | emulated="" 18 | if [ "$target_arch" != "$host_arch" ]; then 19 | emulated=1 20 | echo "target is emulated" 21 | fi 22 | 23 | # This directory needs to exist before calling docker, otherwise docker will create it but it 24 | # will be owned by root 25 | mkdir -p target 26 | 27 | set_env="HOME=/tmp PATH=\$PATH:/rust/bin:/cargo/bin" 28 | docker build -t "libm-$target" "ci/docker/$target" 29 | docker run \ 30 | --rm \ 31 | --user "$(id -u):$(id -g)" \ 32 | -e CI \ 33 | -e RUSTFLAGS \ 34 | -e CARGO_TERM_COLOR \ 35 | -e CARGO_HOME=/cargo \ 36 | -e CARGO_TARGET_DIR=/target \ 37 | -e "EMULATED=$emulated" \ 38 | -v "${HOME}/.cargo:/cargo" \ 39 | -v "$(pwd)/target:/target" \ 40 | -v "$(pwd):/checkout:ro" \ 41 | -v "$(rustc --print sysroot):/rust:ro" \ 42 | --init \ 43 | -w /checkout \ 44 | "libm-$target" \ 45 | sh -c "$set_env exec ci/run.sh $target" 46 | } 47 | 48 | if [ -z "$1" ]; then 49 | echo "running tests for all targets" 50 | 51 | for d in ci/docker/*; do 52 | run $d 53 | done 54 | else 55 | run $1 56 | fi 57 | -------------------------------------------------------------------------------- /crates/compiler-builtins-smoke-test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cb" 3 | version = "0.1.0" 4 | authors = ["Jorge Aparicio "] 5 | edition = "2021" 6 | publish = false 7 | 8 | [lib] 9 | crate-type = ["staticlib"] 10 | test = false 11 | bench = false 12 | 13 | [features] 14 | default = ["arch", "compiler-builtins", "unstable-float"] 15 | 16 | # Copied from `libm`'s root `Cargo.toml`' 17 | arch = [] 18 | compiler-builtins = [] 19 | unstable-float = [] 20 | 21 | [lints.rust] 22 | unexpected_cfgs = { level = "warn", check-cfg = [ 23 | "cfg(arch_enabled)", 24 | "cfg(assert_no_panic)", 25 | "cfg(intrinsics_enabled)", 26 | 'cfg(feature, values("force-soft-floats"))', 27 | 'cfg(feature, values("unstable"))', 28 | 'cfg(feature, values("unstable-intrinsics"))', 29 | 'cfg(feature, values("unstable-public-internals"))', 30 | ] } 31 | 32 | [profile.dev] 33 | panic = "abort" 34 | 35 | [profile.release] 36 | panic = "abort" 37 | codegen-units = 1 38 | lto = "fat" 39 | -------------------------------------------------------------------------------- /crates/compiler-builtins-smoke-test/build.rs: -------------------------------------------------------------------------------- 1 | #[path = "../../libm/configure.rs"] 2 | mod configure; 3 | 4 | fn main() { 5 | println!("cargo:rerun-if-changed=../../libm/configure.rs"); 6 | let cfg = configure::Config::from_env(); 7 | configure::emit_libm_config(&cfg); 8 | } 9 | -------------------------------------------------------------------------------- /crates/compiler-builtins-smoke-test/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Fake compiler-builtins crate 2 | //! 3 | //! This is used to test that we can source import `libm` into the compiler-builtins crate. 4 | //! Additionally, it provides a `#[no_mangle]` C API that can be easier to inspect than the 5 | //! default `.rlib`. 6 | 7 | #![compiler_builtins] 8 | #![feature(core_intrinsics)] 9 | #![feature(compiler_builtins)] 10 | #![feature(f16)] 11 | #![feature(f128)] 12 | #![allow(internal_features)] 13 | #![no_std] 14 | 15 | mod math; 16 | // Required for macro paths. 17 | use math::libm::support; 18 | -------------------------------------------------------------------------------- /crates/libm-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libm-macros" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [lib] 8 | proc-macro = true 9 | 10 | [dependencies] 11 | heck = "0.5.0" 12 | proc-macro2 = "1.0.94" 13 | quote = "1.0.40" 14 | syn = { version = "2.0.100", features = ["full", "extra-traits", "visit-mut"] } 15 | 16 | [lints.rust] 17 | # Values used during testing 18 | unexpected_cfgs = { level = "warn", check-cfg = [ 19 | 'cfg(f16_enabled)', 20 | 'cfg(f128_enabled)', 21 | ] } 22 | -------------------------------------------------------------------------------- /crates/libm-macros/tests/basic.rs: -------------------------------------------------------------------------------- 1 | #![feature(f16)] 2 | #![feature(f128)] 3 | // `STATUS_DLL_NOT_FOUND` on i686 MinGW, not worth looking into. 4 | #![cfg(not(all(target_arch = "x86", target_os = "windows", target_env = "gnu")))] 5 | 6 | macro_rules! basic { 7 | ( 8 | fn_name: $fn_name:ident, 9 | FTy: $FTy:ty, 10 | CFn: $CFn:ty, 11 | CArgs: $CArgs:ty, 12 | CRet: $CRet:ty, 13 | RustFn: $RustFn:ty, 14 | RustArgs: $RustArgs:ty, 15 | RustRet: $RustRet:ty, 16 | attrs: [$($attr:meta),*], 17 | extra: [$($extra_tt:tt)*], 18 | fn_extra: $fn_extra:expr, 19 | ) => { 20 | $(#[$attr])* 21 | #[allow(dead_code)] 22 | pub mod $fn_name { 23 | type FTy= $FTy; 24 | type CFnTy<'a> = $CFn; 25 | type RustFnTy = $RustFn; 26 | type RustArgsTy = $RustArgs; 27 | type RustRetTy = $RustRet; 28 | const A: &[&str] = &[$($extra_tt)*]; 29 | fn foo(a: f32) -> f32 { 30 | $fn_extra(a) 31 | } 32 | } 33 | }; 34 | } 35 | 36 | mod test_basic { 37 | libm_macros::for_each_function! { 38 | callback: basic, 39 | emit_types: all, 40 | skip: [sin, cos], 41 | attributes: [ 42 | // just some random attributes 43 | #[allow(clippy::pedantic)] 44 | #[allow(dead_code)] 45 | [sinf, cosf] 46 | ], 47 | extra: ["foo", "bar"], 48 | fn_extra: match MACRO_FN_NAME { 49 | sin => |x| x + 2.0, 50 | cos | cosf => |x: f32| x.MACRO_FN_NAME_NORMALIZED(), 51 | _ => |_x| 100.0 52 | } 53 | } 54 | } 55 | 56 | macro_rules! basic_no_extra { 57 | ( 58 | fn_name: $fn_name:ident, 59 | attrs: [$($attr:meta),*], 60 | ) => { 61 | $(#[$attr])* 62 | mod $fn_name {} 63 | }; 64 | } 65 | 66 | mod test_basic_no_extra { 67 | // Test with no extra, no skip, and no attributes 68 | libm_macros::for_each_function! { 69 | callback: basic_no_extra, 70 | } 71 | } 72 | 73 | mod test_only { 74 | // Test that only works 75 | libm_macros::for_each_function! { 76 | callback: basic_no_extra, 77 | only: [sin, sinf], 78 | } 79 | } 80 | 81 | macro_rules! specified_types { 82 | ( 83 | fn_name: $fn_name:ident, 84 | RustFn: $RustFn:ty, 85 | RustArgs: $RustArgs:ty, 86 | attrs: [$($attr:meta),*], 87 | ) => { 88 | $(#[$attr])* 89 | #[allow(dead_code)] 90 | mod $fn_name { 91 | type RustFnTy = $RustFn; 92 | type RustArgsTy = $RustArgs; 93 | } 94 | }; 95 | } 96 | 97 | mod test_emit_types { 98 | // Test that we can specify a couple types to emit 99 | libm_macros::for_each_function! { 100 | callback: specified_types, 101 | emit_types: [RustFn, RustArgs], 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /crates/libm-macros/tests/enum.rs: -------------------------------------------------------------------------------- 1 | #[libm_macros::function_enum(BaseName)] 2 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 3 | pub enum Identifier {} 4 | 5 | #[libm_macros::base_name_enum] 6 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 7 | pub enum BaseName {} 8 | 9 | #[test] 10 | fn as_str() { 11 | assert_eq!(Identifier::Sin.as_str(), "sin"); 12 | assert_eq!(Identifier::Sinf.as_str(), "sinf"); 13 | } 14 | 15 | #[test] 16 | fn from_str() { 17 | assert_eq!(Identifier::from_str("sin").unwrap(), Identifier::Sin); 18 | assert_eq!(Identifier::from_str("sinf").unwrap(), Identifier::Sinf); 19 | } 20 | 21 | #[test] 22 | fn basename() { 23 | assert_eq!(Identifier::Sin.base_name(), BaseName::Sin); 24 | assert_eq!(Identifier::Sinf.base_name(), BaseName::Sin); 25 | } 26 | 27 | #[test] 28 | fn math_op() { 29 | assert_eq!(Identifier::Sin.math_op().float_ty, FloatTy::F64); 30 | assert_eq!(Identifier::Sinf.math_op().float_ty, FloatTy::F32); 31 | } 32 | 33 | // Replicate the structure that we have in `libm-test` 34 | mod op { 35 | include!("../../libm-macros/src/shared.rs"); 36 | } 37 | 38 | use op::FloatTy; 39 | -------------------------------------------------------------------------------- /crates/libm-test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libm-test" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [features] 8 | default = ["build-mpfr", "build-musl", "unstable-float"] 9 | 10 | # Propagated from libm because this affects which functions we test. 11 | unstable-float = ["libm/unstable-float", "rug?/nightly-float"] 12 | 13 | # Generate tests which are random inputs and the outputs are calculated with 14 | # musl libc. 15 | build-mpfr = ["dep:rug", "dep:gmp-mpfr-sys"] 16 | 17 | # Build our own musl for testing and benchmarks 18 | build-musl = ["dep:musl-math-sys"] 19 | 20 | # Enable report generation without bringing in more dependencies by default 21 | benchmarking-reports = ["criterion/plotters", "criterion/html_reports"] 22 | 23 | # Enable icount benchmarks (requires iai-callgrind and valgrind) 24 | icount = ["dep:iai-callgrind"] 25 | 26 | # Run with a reduced set of benchmarks, such as for CI 27 | short-benchmarks = [] 28 | 29 | [dependencies] 30 | anyhow = "1.0.97" 31 | # This is not directly used but is required so we can enable `gmp-mpfr-sys/force-cross`. 32 | gmp-mpfr-sys = { version = "1.6.4", optional = true, default-features = false } 33 | iai-callgrind = { version = "0.14.0", optional = true } 34 | indicatif = { version = "0.17.11", default-features = false } 35 | libm = { path = "../../libm", features = ["unstable-public-internals"] } 36 | libm-macros = { path = "../libm-macros" } 37 | musl-math-sys = { path = "../musl-math-sys", optional = true } 38 | paste = "1.0.15" 39 | rand = "0.9.0" 40 | rand_chacha = "0.9.0" 41 | rayon = "1.10.0" 42 | rug = { version = "1.27.0", optional = true, default-features = false, features = ["float", "integer", "std"] } 43 | 44 | [build-dependencies] 45 | rand = { version = "0.9.0", optional = true } 46 | 47 | [dev-dependencies] 48 | criterion = { version = "0.5.1", default-features = false, features = ["cargo_bench_support"] } 49 | libtest-mimic = "0.8.1" 50 | 51 | [[bench]] 52 | name = "icount" 53 | harness = false 54 | required-features = ["icount"] 55 | 56 | [[bench]] 57 | name = "random" 58 | harness = false 59 | 60 | [[test]] 61 | # No harness so that we can skip tests at runtime based on env. Prefixed with 62 | # `z` so these tests get run last. 63 | name = "z_extensive" 64 | harness = false 65 | 66 | [lints.rust] 67 | # Values from the chared config.rs used by `libm` but not the test crate 68 | unexpected_cfgs = { level = "warn", check-cfg = [ 69 | 'cfg(feature, values("arch", "force-soft-floats", "unstable-intrinsics"))', 70 | ] } 71 | -------------------------------------------------------------------------------- /crates/libm-test/build.rs: -------------------------------------------------------------------------------- 1 | #[path = "../../libm/configure.rs"] 2 | mod configure; 3 | use configure::Config; 4 | 5 | fn main() { 6 | println!("cargo:rerun-if-changed=../../libm/configure.rs"); 7 | let cfg = Config::from_env(); 8 | configure::emit_test_config(&cfg); 9 | } 10 | -------------------------------------------------------------------------------- /crates/libm-test/examples/plot_domains.rs: -------------------------------------------------------------------------------- 1 | //! Program to write all inputs from a generator to a file, then invoke a Julia script to plot 2 | //! them. Output is in `target/plots`. 3 | //! 4 | //! Requires Julia with the `CairoMakie` dependency. 5 | //! 6 | //! Note that running in release mode by default generates a _lot_ more datapoints, which 7 | //! causes plotting to be extremely slow (some simplification to be done in the script). 8 | 9 | use std::fmt::Write as _; 10 | use std::io::{BufWriter, Write}; 11 | use std::path::Path; 12 | use std::process::Command; 13 | use std::{env, fs}; 14 | 15 | use libm_test::generate::spaced::SpacedInput; 16 | use libm_test::generate::{edge_cases, spaced}; 17 | use libm_test::{CheckBasis, CheckCtx, GeneratorKind, MathOp, op}; 18 | 19 | const JL_PLOT: &str = "examples/plot_file.jl"; 20 | 21 | fn main() { 22 | let manifest_env = env::var("CARGO_MANIFEST_DIR").unwrap(); 23 | let manifest_dir = Path::new(&manifest_env); 24 | let out_dir = manifest_dir.join("../../target/plots"); 25 | if !out_dir.exists() { 26 | fs::create_dir(&out_dir).unwrap(); 27 | } 28 | 29 | let jl_script = manifest_dir.join(JL_PLOT); 30 | let mut config = format!(r#"out_dir = "{}""#, out_dir.display()); 31 | config.write_str("\n\n").unwrap(); 32 | 33 | // Plot a few domains with some functions that use them. 34 | plot_one_operator::(&out_dir, &mut config); 35 | plot_one_operator::(&out_dir, &mut config); 36 | plot_one_operator::(&out_dir, &mut config); 37 | 38 | let config_path = out_dir.join("config.toml"); 39 | fs::write(&config_path, config).unwrap(); 40 | 41 | // The script expects a path to `config.toml` to be passed as its only argument 42 | let mut cmd = Command::new("julia"); 43 | if cfg!(optimizations_enabled) { 44 | cmd.arg("-O3"); 45 | } 46 | cmd.arg(jl_script).arg(config_path); 47 | 48 | println!("launching script... {cmd:?}"); 49 | cmd.status().unwrap(); 50 | } 51 | 52 | /// Run multiple generators for a single operator. 53 | fn plot_one_operator(out_dir: &Path, config: &mut String) 54 | where 55 | Op: MathOp, 56 | Op::RustArgs: SpacedInput, 57 | { 58 | let mut ctx = CheckCtx::new(Op::IDENTIFIER, CheckBasis::Mpfr, GeneratorKind::QuickSpaced); 59 | plot_one_generator(out_dir, &ctx, "logspace", config, spaced::get_test_cases::(&ctx).0); 60 | ctx.gen_kind = GeneratorKind::EdgeCases; 61 | plot_one_generator( 62 | out_dir, 63 | &ctx, 64 | "edge_cases", 65 | config, 66 | edge_cases::get_test_cases::(&ctx).0, 67 | ); 68 | } 69 | 70 | /// Plot the output of a single generator. 71 | fn plot_one_generator( 72 | out_dir: &Path, 73 | ctx: &CheckCtx, 74 | gen_name: &str, 75 | config: &mut String, 76 | generator: impl Iterator, 77 | ) { 78 | let fn_name = ctx.base_name_str; 79 | let text_file = out_dir.join(format!("input-{fn_name}-{gen_name}.txt")); 80 | 81 | let f = fs::File::create(&text_file).unwrap(); 82 | let mut w = BufWriter::new(f); 83 | let mut count = 0u64; 84 | 85 | for input in generator { 86 | writeln!(w, "{:e}", input.0).unwrap(); 87 | count += 1; 88 | } 89 | 90 | w.flush().unwrap(); 91 | println!("generated {count} inputs for {fn_name}-{gen_name}"); 92 | 93 | writeln!( 94 | config, 95 | r#"[[input]] 96 | function = "{fn_name}" 97 | generator = "{gen_name}" 98 | input_file = "{}" 99 | "#, 100 | text_file.to_str().unwrap() 101 | ) 102 | .unwrap() 103 | } 104 | -------------------------------------------------------------------------------- /crates/libm-test/src/generate.rs: -------------------------------------------------------------------------------- 1 | //! Different generators that can create random or systematic bit patterns. 2 | 3 | pub mod case_list; 4 | pub mod edge_cases; 5 | pub mod random; 6 | pub mod spaced; 7 | 8 | /// A wrapper to turn any iterator into an `ExactSizeIterator`. Asserts the final result to ensure 9 | /// the provided size was correct. 10 | #[derive(Debug)] 11 | pub struct KnownSize { 12 | total: u64, 13 | current: u64, 14 | iter: I, 15 | } 16 | 17 | impl KnownSize { 18 | pub fn new(iter: I, total: u64) -> Self { 19 | Self { total, current: 0, iter } 20 | } 21 | } 22 | 23 | impl Iterator for KnownSize { 24 | type Item = I::Item; 25 | 26 | fn next(&mut self) -> Option { 27 | let next = self.iter.next(); 28 | if next.is_some() { 29 | self.current += 1; 30 | return next; 31 | } 32 | 33 | assert_eq!(self.current, self.total, "total items did not match expected"); 34 | None 35 | } 36 | 37 | fn size_hint(&self) -> (usize, Option) { 38 | let remaining = usize::try_from(self.total - self.current).unwrap(); 39 | (remaining, Some(remaining)) 40 | } 41 | } 42 | 43 | impl ExactSizeIterator for KnownSize {} 44 | -------------------------------------------------------------------------------- /crates/libm-test/tests/check_coverage.rs: -------------------------------------------------------------------------------- 1 | //! Ensure that `for_each_function!` isn't missing any symbols. 2 | 3 | use std::collections::HashSet; 4 | use std::env; 5 | use std::path::Path; 6 | use std::process::Command; 7 | 8 | macro_rules! callback { 9 | ( 10 | fn_name: $name:ident, 11 | attrs: [$($attr:meta),*], 12 | extra: [$set:ident], 13 | ) => { 14 | let name = stringify!($name); 15 | let new = $set.insert(name); 16 | assert!(new, "duplicate function `{name}` in `ALL_OPERATIONS`"); 17 | }; 18 | } 19 | 20 | #[test] 21 | fn test_for_each_function_all_included() { 22 | let all_functions: HashSet<_> = include_str!("../../../etc/function-list.txt") 23 | .lines() 24 | .filter(|line| !line.starts_with("#")) 25 | .collect(); 26 | 27 | let mut tested = HashSet::new(); 28 | 29 | libm_macros::for_each_function! { 30 | callback: callback, 31 | extra: [tested], 32 | }; 33 | 34 | let untested = all_functions.difference(&tested); 35 | if untested.clone().next().is_some() { 36 | panic!( 37 | "missing tests for the following: {untested:#?} \ 38 | \nmake sure any new functions are entered in \ 39 | `ALL_OPERATIONS` (in `libm-macros`)." 40 | ); 41 | } 42 | assert_eq!(all_functions, tested); 43 | } 44 | 45 | #[test] 46 | fn ensure_list_updated() { 47 | if libm_test::ci() { 48 | // Most CI tests run in Docker where we don't have Python or Rustdoc, so it's easiest 49 | // to just run the python file directly when it is available. 50 | eprintln!("skipping test; CI runs the python file directly"); 51 | return; 52 | } 53 | 54 | let res = Command::new("python3") 55 | .arg(Path::new(env!("CARGO_MANIFEST_DIR")).join("../../etc/update-api-list.py")) 56 | .arg("--check") 57 | .status() 58 | .unwrap(); 59 | 60 | assert!(res.success(), "May need to run `./etc/update-api-list.py`"); 61 | } 62 | -------------------------------------------------------------------------------- /crates/libm-test/tests/multiprecision.rs: -------------------------------------------------------------------------------- 1 | //! Test with "infinite precision" 2 | 3 | #![cfg(feature = "build-mpfr")] 4 | 5 | use libm_test::generate::{case_list, edge_cases, random, spaced}; 6 | use libm_test::mpfloat::MpOp; 7 | use libm_test::{CheckBasis, CheckCtx, CheckOutput, GeneratorKind, MathOp, TupleCall}; 8 | 9 | const BASIS: CheckBasis = CheckBasis::Mpfr; 10 | 11 | fn mp_runner(ctx: &CheckCtx, cases: impl Iterator) { 12 | let mut mp_vals = Op::new_mp(); 13 | for input in cases { 14 | let mp_res = Op::run(&mut mp_vals, input); 15 | let crate_res = input.call_intercept_panics(Op::ROUTINE); 16 | 17 | crate_res.validate(mp_res, input, ctx).unwrap(); 18 | } 19 | } 20 | 21 | macro_rules! mp_tests { 22 | ( 23 | fn_name: $fn_name:ident, 24 | attrs: [$($attr:meta),*], 25 | ) => { 26 | paste::paste! { 27 | #[test] 28 | $(#[$attr])* 29 | fn [< mp_case_list_ $fn_name >]() { 30 | type Op = libm_test::op::$fn_name::Routine; 31 | let ctx = CheckCtx::new(Op::IDENTIFIER, BASIS, GeneratorKind::List); 32 | let cases = case_list::get_test_cases_basis::(&ctx).0; 33 | mp_runner::(&ctx, cases); 34 | } 35 | 36 | #[test] 37 | $(#[$attr])* 38 | fn [< mp_random_ $fn_name >]() { 39 | type Op = libm_test::op::$fn_name::Routine; 40 | let ctx = CheckCtx::new(Op::IDENTIFIER, BASIS, GeneratorKind::Random); 41 | let cases = random::get_test_cases::<::RustArgs>(&ctx).0; 42 | mp_runner::(&ctx, cases); 43 | } 44 | 45 | #[test] 46 | $(#[$attr])* 47 | fn [< mp_edge_case_ $fn_name >]() { 48 | type Op = libm_test::op::$fn_name::Routine; 49 | let ctx = CheckCtx::new(Op::IDENTIFIER, BASIS, GeneratorKind::EdgeCases); 50 | let cases = edge_cases::get_test_cases::(&ctx).0; 51 | mp_runner::(&ctx, cases); 52 | } 53 | 54 | #[test] 55 | $(#[$attr])* 56 | fn [< mp_quickspace_ $fn_name >]() { 57 | type Op = libm_test::op::$fn_name::Routine; 58 | let ctx = CheckCtx::new(Op::IDENTIFIER, BASIS, GeneratorKind::QuickSpaced); 59 | let cases = spaced::get_test_cases::(&ctx).0; 60 | mp_runner::(&ctx, cases); 61 | } 62 | } 63 | }; 64 | } 65 | 66 | libm_macros::for_each_function! { 67 | callback: mp_tests, 68 | attributes: [ 69 | // Also an assertion failure on i686: at `MPFR_ASSERTN (! mpfr_erangeflag_p ())` 70 | #[ignore = "large values are infeasible in MPFR"] 71 | [jn, jnf, yn, ynf], 72 | ], 73 | skip: [ 74 | // FIXME: test needed, see 75 | // https://github.com/rust-lang/libm/pull/311#discussion_r1818273392 76 | nextafter, 77 | nextafterf, 78 | ], 79 | } 80 | -------------------------------------------------------------------------------- /crates/libm-test/tests/standalone.rs: -------------------------------------------------------------------------------- 1 | //! Test cases that have both an input and an output, so do not require a basis. 2 | 3 | use libm_test::generate::case_list; 4 | use libm_test::{CheckBasis, CheckCtx, CheckOutput, GeneratorKind, MathOp, TupleCall}; 5 | 6 | const BASIS: CheckBasis = CheckBasis::None; 7 | 8 | fn standalone_runner( 9 | ctx: &CheckCtx, 10 | cases: impl Iterator, 11 | ) { 12 | for (input, expected) in cases { 13 | let crate_res = input.call_intercept_panics(Op::ROUTINE); 14 | crate_res.validate(expected, input, ctx).unwrap(); 15 | } 16 | } 17 | 18 | macro_rules! mp_tests { 19 | ( 20 | fn_name: $fn_name:ident, 21 | attrs: [$($attr:meta),*], 22 | ) => { 23 | paste::paste! { 24 | #[test] 25 | $(#[$attr])* 26 | fn [< standalone_ $fn_name >]() { 27 | type Op = libm_test::op::$fn_name::Routine; 28 | let ctx = CheckCtx::new(Op::IDENTIFIER, BASIS, GeneratorKind::List); 29 | let cases = case_list::get_test_cases_standalone::(&ctx); 30 | standalone_runner::(&ctx, cases); 31 | } 32 | } 33 | }; 34 | } 35 | 36 | libm_macros::for_each_function! { 37 | callback: mp_tests, 38 | } 39 | -------------------------------------------------------------------------------- /crates/libm-test/tests/z_extensive/main.rs: -------------------------------------------------------------------------------- 1 | //! `main` is just a wrapper to handle configuration. 2 | 3 | #[cfg(not(feature = "build-mpfr"))] 4 | fn main() { 5 | eprintln!("multiprecision not enabled; skipping extensive tests"); 6 | } 7 | 8 | #[cfg(feature = "build-mpfr")] 9 | mod run; 10 | 11 | #[cfg(feature = "build-mpfr")] 12 | fn main() { 13 | run::run(); 14 | } 15 | -------------------------------------------------------------------------------- /crates/musl-math-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "musl-math-sys" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [dependencies] 8 | 9 | [dev-dependencies] 10 | libm = { path = "../../libm" } 11 | 12 | [build-dependencies] 13 | cc = "1.2.16" 14 | -------------------------------------------------------------------------------- /crates/musl-math-sys/c_patches/alias.c: -------------------------------------------------------------------------------- 1 | /* On platforms that don't support weak symbols, define required aliases 2 | * as wrappers. See comments in `features.h` for more. 3 | */ 4 | #if defined(__APPLE__) || defined(__MINGW32__) 5 | 6 | double __lgamma_r(double a, int *b); 7 | float __lgammaf_r(float a, int *b); 8 | long __lgammal_r(long double a, int *b); 9 | double exp10(double a); 10 | float exp10f(float a); 11 | long exp10l(long double a); 12 | double remainder(double a, double b); 13 | float remainderf(float a, float b); 14 | 15 | double lgamma_r(double a, int *b) { 16 | return __lgamma_r(a, b); 17 | } 18 | float lgammaf_r(float a, int *b) { 19 | return __lgammaf_r(a, b); 20 | } 21 | long double lgammal_r(long double a, int *b) { 22 | return __lgammal_r(a, b); 23 | } 24 | double pow10(double a) { 25 | return exp10(a); 26 | } 27 | float pow10f(float a) { 28 | return exp10f(a); 29 | } 30 | long double pow10l(long double a) { 31 | return exp10l(a); 32 | } 33 | double drem(double a, double b) { 34 | return remainder(a, b); 35 | } 36 | float dremf(float a, float b) { 37 | return remainderf(a, b); 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /crates/musl-math-sys/c_patches/features.h: -------------------------------------------------------------------------------- 1 | /* This is meant to override Musl's src/include/features.h 2 | * 3 | * We use a separate file here to redefine some attributes that don't work on 4 | * all platforms that we would like to build on. 5 | */ 6 | 7 | #ifndef FEATURES_H 8 | #define FEATURES_H 9 | 10 | /* Get the required `#include "../../include/features.h"` since we can't use 11 | * the relative path. The C macros need double indirection to get a usable 12 | * string. */ 13 | #define _stringify_inner(s) #s 14 | #define _stringify(s) _stringify_inner(s) 15 | #include _stringify(ROOT_INCLUDE_FEATURES) 16 | 17 | #if defined(__APPLE__) 18 | #define weak __attribute__((__weak__)) 19 | #define hidden __attribute__((__visibility__("hidden"))) 20 | 21 | /* We _should_ be able to define this as: 22 | * _Pragma(_stringify(weak musl_ ## new = musl_ ## old)) 23 | * However, weak symbols aren't handled correctly [1]. So we manually write 24 | * wrappers, which are in `alias.c`. 25 | * 26 | * [1]: https://github.com/llvm/llvm-project/issues/111321 27 | */ 28 | #define weak_alias(old, new) /* nothing */ 29 | 30 | #else 31 | #define weak __attribute__((__weak__)) 32 | #define hidden __attribute__((__visibility__("hidden"))) 33 | #define weak_alias(old, new) \ 34 | extern __typeof(old) musl_ ## new \ 35 | __attribute__((__weak__, __alias__(_stringify(musl_ ## old)))) 36 | 37 | #endif /* defined(__APPLE__) */ 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /crates/util/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "util" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [features] 8 | default = ["build-musl", "build-mpfr", "unstable-float"] 9 | build-musl = ["libm-test/build-musl", "dep:musl-math-sys"] 10 | build-mpfr = ["libm-test/build-mpfr", "dep:rug"] 11 | unstable-float = ["libm/unstable-float", "libm-test/unstable-float", "rug?/nightly-float"] 12 | 13 | [dependencies] 14 | libm = { path = "../../libm", default-features = false } 15 | libm-macros = { path = "../libm-macros" } 16 | libm-test = { path = "../libm-test", default-features = false } 17 | musl-math-sys = { path = "../musl-math-sys", optional = true } 18 | rug = { version = "1.27.0", optional = true, default-features = false, features = ["float", "std"] } 19 | -------------------------------------------------------------------------------- /crates/util/build.rs: -------------------------------------------------------------------------------- 1 | #![allow(unexpected_cfgs)] 2 | 3 | #[path = "../../libm/configure.rs"] 4 | mod configure; 5 | 6 | fn main() { 7 | println!("cargo:rerun-if-changed=../../libm/configure.rs"); 8 | let cfg = configure::Config::from_env(); 9 | configure::emit_libm_config(&cfg); 10 | } 11 | -------------------------------------------------------------------------------- /etc/function-list.txt: -------------------------------------------------------------------------------- 1 | # autogenerated by update-api-list.py 2 | acos 3 | acosf 4 | acosh 5 | acoshf 6 | asin 7 | asinf 8 | asinh 9 | asinhf 10 | atan 11 | atan2 12 | atan2f 13 | atanf 14 | atanh 15 | atanhf 16 | cbrt 17 | cbrtf 18 | ceil 19 | ceilf 20 | ceilf128 21 | ceilf16 22 | copysign 23 | copysignf 24 | copysignf128 25 | copysignf16 26 | cos 27 | cosf 28 | cosh 29 | coshf 30 | erf 31 | erfc 32 | erfcf 33 | erff 34 | exp 35 | exp10 36 | exp10f 37 | exp2 38 | exp2f 39 | expf 40 | expm1 41 | expm1f 42 | fabs 43 | fabsf 44 | fabsf128 45 | fabsf16 46 | fdim 47 | fdimf 48 | fdimf128 49 | fdimf16 50 | floor 51 | floorf 52 | floorf128 53 | floorf16 54 | fma 55 | fmaf 56 | fmaf128 57 | fmax 58 | fmaxf 59 | fmaxf128 60 | fmaxf16 61 | fmaximum 62 | fmaximum_num 63 | fmaximum_numf 64 | fmaximum_numf128 65 | fmaximum_numf16 66 | fmaximumf 67 | fmaximumf128 68 | fmaximumf16 69 | fmin 70 | fminf 71 | fminf128 72 | fminf16 73 | fminimum 74 | fminimum_num 75 | fminimum_numf 76 | fminimum_numf128 77 | fminimum_numf16 78 | fminimumf 79 | fminimumf128 80 | fminimumf16 81 | fmod 82 | fmodf 83 | fmodf128 84 | fmodf16 85 | frexp 86 | frexpf 87 | hypot 88 | hypotf 89 | ilogb 90 | ilogbf 91 | j0 92 | j0f 93 | j1 94 | j1f 95 | jn 96 | jnf 97 | ldexp 98 | ldexpf 99 | ldexpf128 100 | ldexpf16 101 | lgamma 102 | lgamma_r 103 | lgammaf 104 | lgammaf_r 105 | log 106 | log10 107 | log10f 108 | log1p 109 | log1pf 110 | log2 111 | log2f 112 | logf 113 | modf 114 | modff 115 | nextafter 116 | nextafterf 117 | pow 118 | powf 119 | remainder 120 | remainderf 121 | remquo 122 | remquof 123 | rint 124 | rintf 125 | rintf128 126 | rintf16 127 | round 128 | roundeven 129 | roundevenf 130 | roundevenf128 131 | roundevenf16 132 | roundf 133 | roundf128 134 | roundf16 135 | scalbn 136 | scalbnf 137 | scalbnf128 138 | scalbnf16 139 | sin 140 | sincos 141 | sincosf 142 | sinf 143 | sinh 144 | sinhf 145 | sqrt 146 | sqrtf 147 | sqrtf128 148 | sqrtf16 149 | tan 150 | tanf 151 | tanh 152 | tanhf 153 | tgamma 154 | tgammaf 155 | trunc 156 | truncf 157 | truncf128 158 | truncf16 159 | y0 160 | y0f 161 | y1 162 | y1f 163 | yn 164 | ynf 165 | -------------------------------------------------------------------------------- /libm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Jorge Aparicio "] 3 | categories = ["no-std"] 4 | description = "libm in pure Rust" 5 | documentation = "https://docs.rs/libm" 6 | keywords = ["libm", "math"] 7 | license = "MIT" 8 | name = "libm" 9 | readme = "README.md" 10 | repository = "https://github.com/rust-lang/libm" 11 | version = "0.2.11" 12 | edition = "2021" 13 | rust-version = "1.63" 14 | 15 | [features] 16 | default = ["arch"] 17 | 18 | # Enable architecture-specific features such as SIMD or assembly routines. 19 | arch = [] 20 | 21 | # This tells the compiler to assume that a Nightly toolchain is being used and 22 | # that it should activate any useful Nightly things accordingly. 23 | unstable = ["unstable-intrinsics", "unstable-float"] 24 | 25 | # Enable calls to functions in `core::intrinsics` 26 | unstable-intrinsics = [] 27 | 28 | # Make some internal things public for testing. 29 | unstable-public-internals = [] 30 | 31 | # Enable the nightly-only `f16` and `f128`. 32 | unstable-float = [] 33 | 34 | # Used to prevent using any intrinsics or arch-specific code. 35 | # 36 | # HACK: this is a negative feature which is generally a bad idea in Cargo, but 37 | # we need it to be able to forbid other features when this crate is used in 38 | # Rust dependencies. Setting this overrides all features that may enable 39 | # hard float operations. 40 | force-soft-floats = [] 41 | 42 | [dev-dependencies] 43 | no-panic = "0.1.35" 44 | 45 | [lints.rust] 46 | unexpected_cfgs = { level = "warn", check-cfg = [ 47 | # compiler-builtins sets this feature, but we use it in `libm` 48 | 'cfg(feature, values("compiler-builtins"))', 49 | ] } 50 | -------------------------------------------------------------------------------- /libm/LICENSE.txt: -------------------------------------------------------------------------------- 1 | ../LICENSE.txt -------------------------------------------------------------------------------- /libm/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /libm/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | mod configure; 4 | 5 | fn main() { 6 | let cfg = configure::Config::from_env(); 7 | 8 | println!("cargo:rerun-if-changed=build.rs"); 9 | println!("cargo:rerun-if-changed=configure.rs"); 10 | println!("cargo:rustc-check-cfg=cfg(assert_no_panic)"); 11 | 12 | // If set, enable `no-panic`. Requires LTO (`release-opt` profile). 13 | if env::var("ENSURE_NO_PANIC").is_ok() { 14 | println!("cargo:rustc-cfg=assert_no_panic"); 15 | } 16 | 17 | configure::emit_libm_config(&cfg); 18 | } 19 | -------------------------------------------------------------------------------- /libm/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! libm in pure Rust 2 | #![no_std] 3 | #![cfg_attr(intrinsics_enabled, allow(internal_features))] 4 | #![cfg_attr(intrinsics_enabled, feature(core_intrinsics))] 5 | #![cfg_attr(all(intrinsics_enabled, target_family = "wasm"), feature(wasm_numeric_instr))] 6 | #![cfg_attr(f128_enabled, feature(f128))] 7 | #![cfg_attr(f16_enabled, feature(f16))] 8 | #![allow(clippy::assign_op_pattern)] 9 | #![allow(clippy::deprecated_cfg_attr)] 10 | #![allow(clippy::eq_op)] 11 | #![allow(clippy::excessive_precision)] 12 | #![allow(clippy::float_cmp)] 13 | #![allow(clippy::int_plus_one)] 14 | #![allow(clippy::many_single_char_names)] 15 | #![allow(clippy::mixed_case_hex_literals)] 16 | #![allow(clippy::needless_late_init)] 17 | #![allow(clippy::needless_return)] 18 | #![allow(clippy::unreadable_literal)] 19 | #![allow(clippy::zero_divided_by_zero)] 20 | #![forbid(unsafe_op_in_unsafe_fn)] 21 | 22 | mod libm_helper; 23 | mod math; 24 | 25 | use core::{f32, f64}; 26 | 27 | pub use libm_helper::*; 28 | 29 | pub use self::math::*; 30 | -------------------------------------------------------------------------------- /libm/src/math/acosf.rs: -------------------------------------------------------------------------------- 1 | /* origin: FreeBSD /usr/src/lib/msun/src/e_acosf.c */ 2 | /* 3 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 4 | */ 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | use super::sqrt::sqrtf; 17 | 18 | const PIO2_HI: f32 = 1.5707962513e+00; /* 0x3fc90fda */ 19 | const PIO2_LO: f32 = 7.5497894159e-08; /* 0x33a22168 */ 20 | const P_S0: f32 = 1.6666586697e-01; 21 | const P_S1: f32 = -4.2743422091e-02; 22 | const P_S2: f32 = -8.6563630030e-03; 23 | const Q_S1: f32 = -7.0662963390e-01; 24 | 25 | fn r(z: f32) -> f32 { 26 | let p = z * (P_S0 + z * (P_S1 + z * P_S2)); 27 | let q = 1. + z * Q_S1; 28 | p / q 29 | } 30 | 31 | /// Arccosine (f32) 32 | /// 33 | /// Computes the inverse cosine (arc cosine) of the input value. 34 | /// Arguments must be in the range -1 to 1. 35 | /// Returns values in radians, in the range of 0 to pi. 36 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 37 | pub fn acosf(x: f32) -> f32 { 38 | let x1p_120 = f32::from_bits(0x03800000); // 0x1p-120 === 2 ^ (-120) 39 | 40 | let z: f32; 41 | let w: f32; 42 | let s: f32; 43 | 44 | let mut hx = x.to_bits(); 45 | let ix = hx & 0x7fffffff; 46 | /* |x| >= 1 or nan */ 47 | if ix >= 0x3f800000 { 48 | if ix == 0x3f800000 { 49 | if (hx >> 31) != 0 { 50 | return 2. * PIO2_HI + x1p_120; 51 | } 52 | return 0.; 53 | } 54 | return 0. / (x - x); 55 | } 56 | /* |x| < 0.5 */ 57 | if ix < 0x3f000000 { 58 | if ix <= 0x32800000 { 59 | /* |x| < 2**-26 */ 60 | return PIO2_HI + x1p_120; 61 | } 62 | return PIO2_HI - (x - (PIO2_LO - x * r(x * x))); 63 | } 64 | /* x < -0.5 */ 65 | if (hx >> 31) != 0 { 66 | z = (1. + x) * 0.5; 67 | s = sqrtf(z); 68 | w = r(z) * s - PIO2_LO; 69 | return 2. * (PIO2_HI - (s + w)); 70 | } 71 | /* x > 0.5 */ 72 | z = (1. - x) * 0.5; 73 | s = sqrtf(z); 74 | hx = s.to_bits(); 75 | let df = f32::from_bits(hx & 0xfffff000); 76 | let c = (z - df * df) / (s + df); 77 | w = r(z) * s + c; 78 | 2. * (df + w) 79 | } 80 | -------------------------------------------------------------------------------- /libm/src/math/acosh.rs: -------------------------------------------------------------------------------- 1 | use super::{log, log1p, sqrt}; 2 | 3 | const LN2: f64 = 0.693147180559945309417232121458176568; /* 0x3fe62e42, 0xfefa39ef*/ 4 | 5 | /// Inverse hyperbolic cosine (f64) 6 | /// 7 | /// Calculates the inverse hyperbolic cosine of `x`. 8 | /// Is defined as `log(x + sqrt(x*x-1))`. 9 | /// `x` must be a number greater than or equal to 1. 10 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 11 | pub fn acosh(x: f64) -> f64 { 12 | let u = x.to_bits(); 13 | let e = ((u >> 52) as usize) & 0x7ff; 14 | 15 | /* x < 1 domain error is handled in the called functions */ 16 | 17 | if e < 0x3ff + 1 { 18 | /* |x| < 2, up to 2ulp error in [1,1.125] */ 19 | return log1p(x - 1.0 + sqrt((x - 1.0) * (x - 1.0) + 2.0 * (x - 1.0))); 20 | } 21 | if e < 0x3ff + 26 { 22 | /* |x| < 0x1p26 */ 23 | return log(2.0 * x - 1.0 / (x + sqrt(x * x - 1.0))); 24 | } 25 | /* |x| >= 0x1p26 or nan */ 26 | return log(x) + LN2; 27 | } 28 | -------------------------------------------------------------------------------- /libm/src/math/acoshf.rs: -------------------------------------------------------------------------------- 1 | use super::{log1pf, logf, sqrtf}; 2 | 3 | const LN2: f32 = 0.693147180559945309417232121458176568; 4 | 5 | /// Inverse hyperbolic cosine (f32) 6 | /// 7 | /// Calculates the inverse hyperbolic cosine of `x`. 8 | /// Is defined as `log(x + sqrt(x*x-1))`. 9 | /// `x` must be a number greater than or equal to 1. 10 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 11 | pub fn acoshf(x: f32) -> f32 { 12 | let u = x.to_bits(); 13 | let a = u & 0x7fffffff; 14 | 15 | if a < 0x3f800000 + (1 << 23) { 16 | /* |x| < 2, invalid if x < 1 or nan */ 17 | /* up to 2ulp error in [1,1.125] */ 18 | return log1pf(x - 1.0 + sqrtf((x - 1.0) * (x - 1.0) + 2.0 * (x - 1.0))); 19 | } 20 | if a < 0x3f800000 + (12 << 23) { 21 | /* |x| < 0x1p12 */ 22 | return logf(2.0 * x - 1.0 / (x + sqrtf(x * x - 1.0))); 23 | } 24 | /* x >= 0x1p12 */ 25 | return logf(x) + LN2; 26 | } 27 | -------------------------------------------------------------------------------- /libm/src/math/arch/aarch64.rs: -------------------------------------------------------------------------------- 1 | //! Architecture-specific support for aarch64 with neon. 2 | 3 | use core::arch::asm; 4 | 5 | pub fn fma(mut x: f64, y: f64, z: f64) -> f64 { 6 | // SAFETY: `fmadd` is available with neon and has no side effects. 7 | unsafe { 8 | asm!( 9 | "fmadd {x:d}, {x:d}, {y:d}, {z:d}", 10 | x = inout(vreg) x, 11 | y = in(vreg) y, 12 | z = in(vreg) z, 13 | options(nomem, nostack, pure) 14 | ); 15 | } 16 | x 17 | } 18 | 19 | pub fn fmaf(mut x: f32, y: f32, z: f32) -> f32 { 20 | // SAFETY: `fmadd` is available with neon and has no side effects. 21 | unsafe { 22 | asm!( 23 | "fmadd {x:s}, {x:s}, {y:s}, {z:s}", 24 | x = inout(vreg) x, 25 | y = in(vreg) y, 26 | z = in(vreg) z, 27 | options(nomem, nostack, pure) 28 | ); 29 | } 30 | x 31 | } 32 | 33 | pub fn rint(mut x: f64) -> f64 { 34 | // SAFETY: `frintn` is available with neon and has no side effects. 35 | // 36 | // `frintn` is always round-to-nearest which does not match the C specification, but Rust does 37 | // not support rounding modes. 38 | unsafe { 39 | asm!( 40 | "frintn {x:d}, {x:d}", 41 | x = inout(vreg) x, 42 | options(nomem, nostack, pure) 43 | ); 44 | } 45 | x 46 | } 47 | 48 | pub fn rintf(mut x: f32) -> f32 { 49 | // SAFETY: `frintn` is available with neon and has no side effects. 50 | // 51 | // `frintn` is always round-to-nearest which does not match the C specification, but Rust does 52 | // not support rounding modes. 53 | unsafe { 54 | asm!( 55 | "frintn {x:s}, {x:s}", 56 | x = inout(vreg) x, 57 | options(nomem, nostack, pure) 58 | ); 59 | } 60 | x 61 | } 62 | 63 | #[cfg(all(f16_enabled, target_feature = "fp16"))] 64 | pub fn rintf16(mut x: f16) -> f16 { 65 | // SAFETY: `frintn` is available for `f16` with `fp16` (implies `neon`) and has no side effects. 66 | // 67 | // `frintn` is always round-to-nearest which does not match the C specification, but Rust does 68 | // not support rounding modes. 69 | unsafe { 70 | asm!( 71 | "frintn {x:h}, {x:h}", 72 | x = inout(vreg) x, 73 | options(nomem, nostack, pure) 74 | ); 75 | } 76 | x 77 | } 78 | 79 | pub fn sqrt(mut x: f64) -> f64 { 80 | // SAFETY: `fsqrt` is available with neon and has no side effects. 81 | unsafe { 82 | asm!( 83 | "fsqrt {x:d}, {x:d}", 84 | x = inout(vreg) x, 85 | options(nomem, nostack, pure) 86 | ); 87 | } 88 | x 89 | } 90 | 91 | pub fn sqrtf(mut x: f32) -> f32 { 92 | // SAFETY: `fsqrt` is available with neon and has no side effects. 93 | unsafe { 94 | asm!( 95 | "fsqrt {x:s}, {x:s}", 96 | x = inout(vreg) x, 97 | options(nomem, nostack, pure) 98 | ); 99 | } 100 | x 101 | } 102 | 103 | #[cfg(all(f16_enabled, target_feature = "fp16"))] 104 | pub fn sqrtf16(mut x: f16) -> f16 { 105 | // SAFETY: `fsqrt` is available for `f16` with `fp16` (implies `neon`) and has no 106 | // side effects. 107 | unsafe { 108 | asm!( 109 | "fsqrt {x:h}, {x:h}", 110 | x = inout(vreg) x, 111 | options(nomem, nostack, pure) 112 | ); 113 | } 114 | x 115 | } 116 | -------------------------------------------------------------------------------- /libm/src/math/arch/i586.rs: -------------------------------------------------------------------------------- 1 | //! Architecture-specific support for x86-32 without SSE2 2 | 3 | use super::super::fabs; 4 | 5 | /// Use an alternative implementation on x86, because the 6 | /// main implementation fails with the x87 FPU used by 7 | /// debian i386, probably due to excess precision issues. 8 | /// Basic implementation taken from https://github.com/rust-lang/libm/issues/219. 9 | pub fn ceil(x: f64) -> f64 { 10 | if fabs(x).to_bits() < 4503599627370496.0_f64.to_bits() { 11 | let truncated = x as i64 as f64; 12 | if truncated < x { 13 | return truncated + 1.0; 14 | } else { 15 | return truncated; 16 | } 17 | } else { 18 | return x; 19 | } 20 | } 21 | 22 | /// Use an alternative implementation on x86, because the 23 | /// main implementation fails with the x87 FPU used by 24 | /// debian i386, probably due to excess precision issues. 25 | /// Basic implementation taken from https://github.com/rust-lang/libm/issues/219. 26 | pub fn floor(x: f64) -> f64 { 27 | if fabs(x).to_bits() < 4503599627370496.0_f64.to_bits() { 28 | let truncated = x as i64 as f64; 29 | if truncated > x { 30 | return truncated - 1.0; 31 | } else { 32 | return truncated; 33 | } 34 | } else { 35 | return x; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /libm/src/math/arch/i686.rs: -------------------------------------------------------------------------------- 1 | //! Architecture-specific support for x86-32 and x86-64 with SSE2 2 | 3 | pub fn sqrtf(mut x: f32) -> f32 { 4 | // SAFETY: `sqrtss` is part of `sse2`, which this module is gated behind. It has no memory 5 | // access or side effects. 6 | unsafe { 7 | core::arch::asm!( 8 | "sqrtss {x}, {x}", 9 | x = inout(xmm_reg) x, 10 | options(nostack, nomem, pure), 11 | ) 12 | }; 13 | x 14 | } 15 | 16 | pub fn sqrt(mut x: f64) -> f64 { 17 | // SAFETY: `sqrtsd` is part of `sse2`, which this module is gated behind. It has no memory 18 | // access or side effects. 19 | unsafe { 20 | core::arch::asm!( 21 | "sqrtsd {x}, {x}", 22 | x = inout(xmm_reg) x, 23 | options(nostack, nomem, pure), 24 | ) 25 | }; 26 | x 27 | } 28 | -------------------------------------------------------------------------------- /libm/src/math/arch/mod.rs: -------------------------------------------------------------------------------- 1 | //! Architecture-specific routines and operations. 2 | //! 3 | //! LLVM will already optimize calls to some of these in cases that there are hardware 4 | //! instructions. Providing an implementation here just ensures that the faster implementation 5 | //! is used when calling the function directly. This helps anyone who uses `libm` directly, as 6 | //! well as improving things when these routines are called as part of other implementations. 7 | 8 | // Most implementations should be defined here, to ensure they are not made available when 9 | // soft floats are required. 10 | #[cfg(arch_enabled)] 11 | cfg_if! { 12 | if #[cfg(all(target_arch = "wasm32", intrinsics_enabled))] { 13 | mod wasm32; 14 | pub use wasm32::{ 15 | ceil, ceilf, fabs, fabsf, floor, floorf, rint, rintf, sqrt, sqrtf, trunc, truncf, 16 | }; 17 | } else if #[cfg(target_feature = "sse2")] { 18 | mod i686; 19 | pub use i686::{sqrt, sqrtf}; 20 | } else if #[cfg(all( 21 | any(target_arch = "aarch64", target_arch = "arm64ec"), 22 | target_feature = "neon" 23 | ))] { 24 | mod aarch64; 25 | 26 | pub use aarch64::{ 27 | fma, 28 | fmaf, 29 | rint, 30 | rintf, 31 | sqrt, 32 | sqrtf, 33 | }; 34 | 35 | #[cfg(all(f16_enabled, target_feature = "fp16"))] 36 | pub use aarch64::{ 37 | rintf16, 38 | sqrtf16, 39 | }; 40 | } 41 | } 42 | 43 | // There are certain architecture-specific implementations that are needed for correctness 44 | // even with `force-soft-float`. These are configured here. 45 | cfg_if! { 46 | if #[cfg(all(target_arch = "x86", not(target_feature = "sse2")))] { 47 | mod i586; 48 | pub use i586::{ceil, floor}; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /libm/src/math/arch/wasm32.rs: -------------------------------------------------------------------------------- 1 | //! Wasm has builtins for simple float operations. Use the unstable `core::arch` intrinsics which 2 | //! are significantly faster than soft float operations. 3 | 4 | pub fn ceil(x: f64) -> f64 { 5 | core::arch::wasm32::f64_ceil(x) 6 | } 7 | 8 | pub fn ceilf(x: f32) -> f32 { 9 | core::arch::wasm32::f32_ceil(x) 10 | } 11 | 12 | pub fn fabs(x: f64) -> f64 { 13 | x.abs() 14 | } 15 | 16 | pub fn fabsf(x: f32) -> f32 { 17 | x.abs() 18 | } 19 | 20 | pub fn floor(x: f64) -> f64 { 21 | core::arch::wasm32::f64_floor(x) 22 | } 23 | 24 | pub fn floorf(x: f32) -> f32 { 25 | core::arch::wasm32::f32_floor(x) 26 | } 27 | 28 | pub fn rint(x: f64) -> f64 { 29 | core::arch::wasm32::f64_nearest(x) 30 | } 31 | 32 | pub fn rintf(x: f32) -> f32 { 33 | core::arch::wasm32::f32_nearest(x) 34 | } 35 | 36 | pub fn sqrt(x: f64) -> f64 { 37 | core::arch::wasm32::f64_sqrt(x) 38 | } 39 | 40 | pub fn sqrtf(x: f32) -> f32 { 41 | core::arch::wasm32::f32_sqrt(x) 42 | } 43 | 44 | pub fn trunc(x: f64) -> f64 { 45 | core::arch::wasm32::f64_trunc(x) 46 | } 47 | 48 | pub fn truncf(x: f32) -> f32 { 49 | core::arch::wasm32::f32_trunc(x) 50 | } 51 | -------------------------------------------------------------------------------- /libm/src/math/asinf.rs: -------------------------------------------------------------------------------- 1 | /* origin: FreeBSD /usr/src/lib/msun/src/e_asinf.c */ 2 | /* 3 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 4 | */ 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | use super::sqrt::sqrt; 17 | use super::support::Float; 18 | 19 | const PIO2: f64 = 1.570796326794896558e+00; 20 | 21 | /* coefficients for R(x^2) */ 22 | const P_S0: f32 = 1.6666586697e-01; 23 | const P_S1: f32 = -4.2743422091e-02; 24 | const P_S2: f32 = -8.6563630030e-03; 25 | const Q_S1: f32 = -7.0662963390e-01; 26 | 27 | fn r(z: f32) -> f32 { 28 | let p = z * (P_S0 + z * (P_S1 + z * P_S2)); 29 | let q = 1. + z * Q_S1; 30 | p / q 31 | } 32 | 33 | /// Arcsine (f32) 34 | /// 35 | /// Computes the inverse sine (arc sine) of the argument `x`. 36 | /// Arguments to asin must be in the range -1 to 1. 37 | /// Returns values in radians, in the range of -pi/2 to pi/2. 38 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 39 | pub fn asinf(mut x: f32) -> f32 { 40 | let x1p_120 = f64::from_bits(0x3870000000000000); // 0x1p-120 === 2 ^ (-120) 41 | 42 | let hx = x.to_bits(); 43 | let ix = hx & 0x7fffffff; 44 | 45 | if ix >= 0x3f800000 { 46 | /* |x| >= 1 */ 47 | if ix == 0x3f800000 { 48 | /* |x| == 1 */ 49 | return ((x as f64) * PIO2 + x1p_120) as f32; /* asin(+-1) = +-pi/2 with inexact */ 50 | } 51 | return 0. / (x - x); /* asin(|x|>1) is NaN */ 52 | } 53 | 54 | if ix < 0x3f000000 { 55 | /* |x| < 0.5 */ 56 | /* if 0x1p-126 <= |x| < 0x1p-12, avoid raising underflow */ 57 | if (0x00800000..0x39800000).contains(&ix) { 58 | return x; 59 | } 60 | return x + x * r(x * x); 61 | } 62 | 63 | /* 1 > |x| >= 0.5 */ 64 | let z = (1. - Float::abs(x)) * 0.5; 65 | let s = sqrt(z as f64); 66 | x = (PIO2 - 2. * (s + s * (r(z) as f64))) as f32; 67 | if (hx >> 31) != 0 { -x } else { x } 68 | } 69 | -------------------------------------------------------------------------------- /libm/src/math/asinh.rs: -------------------------------------------------------------------------------- 1 | use super::{log, log1p, sqrt}; 2 | 3 | const LN2: f64 = 0.693147180559945309417232121458176568; /* 0x3fe62e42, 0xfefa39ef*/ 4 | 5 | /* asinh(x) = sign(x)*log(|x|+sqrt(x*x+1)) ~= x - x^3/6 + o(x^5) */ 6 | /// Inverse hyperbolic sine (f64) 7 | /// 8 | /// Calculates the inverse hyperbolic sine of `x`. 9 | /// Is defined as `sgn(x)*log(|x|+sqrt(x*x+1))`. 10 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 11 | pub fn asinh(mut x: f64) -> f64 { 12 | let mut u = x.to_bits(); 13 | let e = ((u >> 52) as usize) & 0x7ff; 14 | let sign = (u >> 63) != 0; 15 | 16 | /* |x| */ 17 | u &= (!0) >> 1; 18 | x = f64::from_bits(u); 19 | 20 | if e >= 0x3ff + 26 { 21 | /* |x| >= 0x1p26 or inf or nan */ 22 | x = log(x) + LN2; 23 | } else if e >= 0x3ff + 1 { 24 | /* |x| >= 2 */ 25 | x = log(2.0 * x + 1.0 / (sqrt(x * x + 1.0) + x)); 26 | } else if e >= 0x3ff - 26 { 27 | /* |x| >= 0x1p-26, up to 1.6ulp error in [0.125,0.5] */ 28 | x = log1p(x + x * x / (sqrt(x * x + 1.0) + 1.0)); 29 | } else { 30 | /* |x| < 0x1p-26, raise inexact if x != 0 */ 31 | let x1p120 = f64::from_bits(0x4770000000000000); 32 | force_eval!(x + x1p120); 33 | } 34 | 35 | if sign { -x } else { x } 36 | } 37 | -------------------------------------------------------------------------------- /libm/src/math/asinhf.rs: -------------------------------------------------------------------------------- 1 | use super::{log1pf, logf, sqrtf}; 2 | 3 | const LN2: f32 = 0.693147180559945309417232121458176568; 4 | 5 | /* asinh(x) = sign(x)*log(|x|+sqrt(x*x+1)) ~= x - x^3/6 + o(x^5) */ 6 | /// Inverse hyperbolic sine (f32) 7 | /// 8 | /// Calculates the inverse hyperbolic sine of `x`. 9 | /// Is defined as `sgn(x)*log(|x|+sqrt(x*x+1))`. 10 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 11 | pub fn asinhf(mut x: f32) -> f32 { 12 | let u = x.to_bits(); 13 | let i = u & 0x7fffffff; 14 | let sign = (u >> 31) != 0; 15 | 16 | /* |x| */ 17 | x = f32::from_bits(i); 18 | 19 | if i >= 0x3f800000 + (12 << 23) { 20 | /* |x| >= 0x1p12 or inf or nan */ 21 | x = logf(x) + LN2; 22 | } else if i >= 0x3f800000 + (1 << 23) { 23 | /* |x| >= 2 */ 24 | x = logf(2.0 * x + 1.0 / (sqrtf(x * x + 1.0) + x)); 25 | } else if i >= 0x3f800000 - (12 << 23) { 26 | /* |x| >= 0x1p-12, up to 1.6ulp error in [0.125,0.5] */ 27 | x = log1pf(x + x * x / (sqrtf(x * x + 1.0) + 1.0)); 28 | } else { 29 | /* |x| < 0x1p-12, raise inexact if x!=0 */ 30 | let x1p120 = f32::from_bits(0x7b800000); 31 | force_eval!(x + x1p120); 32 | } 33 | 34 | if sign { -x } else { x } 35 | } 36 | -------------------------------------------------------------------------------- /libm/src/math/atan2f.rs: -------------------------------------------------------------------------------- 1 | /* origin: FreeBSD /usr/src/lib/msun/src/e_atan2f.c */ 2 | /* 3 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 4 | */ 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | use super::{atanf, fabsf}; 17 | 18 | const PI: f32 = 3.1415927410e+00; /* 0x40490fdb */ 19 | const PI_LO: f32 = -8.7422776573e-08; /* 0xb3bbbd2e */ 20 | 21 | /// Arctangent of y/x (f32) 22 | /// 23 | /// Computes the inverse tangent (arc tangent) of `y/x`. 24 | /// Produces the correct result even for angles near pi/2 or -pi/2 (that is, when `x` is near 0). 25 | /// Returns a value in radians, in the range of -pi to pi. 26 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 27 | pub fn atan2f(y: f32, x: f32) -> f32 { 28 | if x.is_nan() || y.is_nan() { 29 | return x + y; 30 | } 31 | let mut ix = x.to_bits(); 32 | let mut iy = y.to_bits(); 33 | 34 | if ix == 0x3f800000 { 35 | /* x=1.0 */ 36 | return atanf(y); 37 | } 38 | let m = ((iy >> 31) & 1) | ((ix >> 30) & 2); /* 2*sign(x)+sign(y) */ 39 | ix &= 0x7fffffff; 40 | iy &= 0x7fffffff; 41 | 42 | /* when y = 0 */ 43 | if iy == 0 { 44 | return match m { 45 | 0 | 1 => y, /* atan(+-0,+anything)=+-0 */ 46 | 2 => PI, /* atan(+0,-anything) = pi */ 47 | _ => -PI, /* atan(-0,-anything) =-pi */ 48 | }; 49 | } 50 | /* when x = 0 */ 51 | if ix == 0 { 52 | return if m & 1 != 0 { -PI / 2. } else { PI / 2. }; 53 | } 54 | /* when x is INF */ 55 | if ix == 0x7f800000 { 56 | return if iy == 0x7f800000 { 57 | match m { 58 | 0 => PI / 4., /* atan(+INF,+INF) */ 59 | 1 => -PI / 4., /* atan(-INF,+INF) */ 60 | 2 => 3. * PI / 4., /* atan(+INF,-INF)*/ 61 | _ => -3. * PI / 4., /* atan(-INF,-INF)*/ 62 | } 63 | } else { 64 | match m { 65 | 0 => 0., /* atan(+...,+INF) */ 66 | 1 => -0., /* atan(-...,+INF) */ 67 | 2 => PI, /* atan(+...,-INF) */ 68 | _ => -PI, /* atan(-...,-INF) */ 69 | } 70 | }; 71 | } 72 | /* |y/x| > 0x1p26 */ 73 | if (ix + (26 << 23) < iy) || (iy == 0x7f800000) { 74 | return if m & 1 != 0 { -PI / 2. } else { PI / 2. }; 75 | } 76 | 77 | /* z = atan(|y/x|) with correct underflow */ 78 | let z = if (m & 2 != 0) && (iy + (26 << 23) < ix) { 79 | /*|y/x| < 0x1p-26, x < 0 */ 80 | 0. 81 | } else { 82 | atanf(fabsf(y / x)) 83 | }; 84 | match m { 85 | 0 => z, /* atan(+,+) */ 86 | 1 => -z, /* atan(-,+) */ 87 | 2 => PI - (z - PI_LO), /* atan(+,-) */ 88 | _ => (z - PI_LO) - PI, /* case 3 */ /* atan(-,-) */ 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /libm/src/math/atanf.rs: -------------------------------------------------------------------------------- 1 | /* origin: FreeBSD /usr/src/lib/msun/src/s_atanf.c */ 2 | /* 3 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 4 | */ 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | use super::fabsf; 17 | 18 | const ATAN_HI: [f32; 4] = [ 19 | 4.6364760399e-01, /* atan(0.5)hi 0x3eed6338 */ 20 | 7.8539812565e-01, /* atan(1.0)hi 0x3f490fda */ 21 | 9.8279368877e-01, /* atan(1.5)hi 0x3f7b985e */ 22 | 1.5707962513e+00, /* atan(inf)hi 0x3fc90fda */ 23 | ]; 24 | 25 | const ATAN_LO: [f32; 4] = [ 26 | 5.0121582440e-09, /* atan(0.5)lo 0x31ac3769 */ 27 | 3.7748947079e-08, /* atan(1.0)lo 0x33222168 */ 28 | 3.4473217170e-08, /* atan(1.5)lo 0x33140fb4 */ 29 | 7.5497894159e-08, /* atan(inf)lo 0x33a22168 */ 30 | ]; 31 | 32 | const A_T: [f32; 5] = 33 | [3.3333328366e-01, -1.9999158382e-01, 1.4253635705e-01, -1.0648017377e-01, 6.1687607318e-02]; 34 | 35 | /// Arctangent (f32) 36 | /// 37 | /// Computes the inverse tangent (arc tangent) of the input value. 38 | /// Returns a value in radians, in the range of -pi/2 to pi/2. 39 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 40 | pub fn atanf(mut x: f32) -> f32 { 41 | let x1p_120 = f32::from_bits(0x03800000); // 0x1p-120 === 2 ^ (-120) 42 | 43 | let z: f32; 44 | 45 | let mut ix = x.to_bits(); 46 | let sign = (ix >> 31) != 0; 47 | ix &= 0x7fffffff; 48 | 49 | if ix >= 0x4c800000 { 50 | /* if |x| >= 2**26 */ 51 | if x.is_nan() { 52 | return x; 53 | } 54 | z = i!(ATAN_HI, 3) + x1p_120; 55 | return if sign { -z } else { z }; 56 | } 57 | let id = if ix < 0x3ee00000 { 58 | /* |x| < 0.4375 */ 59 | if ix < 0x39800000 { 60 | /* |x| < 2**-12 */ 61 | if ix < 0x00800000 { 62 | /* raise underflow for subnormal x */ 63 | force_eval!(x * x); 64 | } 65 | return x; 66 | } 67 | -1 68 | } else { 69 | x = fabsf(x); 70 | if ix < 0x3f980000 { 71 | /* |x| < 1.1875 */ 72 | if ix < 0x3f300000 { 73 | /* 7/16 <= |x| < 11/16 */ 74 | x = (2. * x - 1.) / (2. + x); 75 | 0 76 | } else { 77 | /* 11/16 <= |x| < 19/16 */ 78 | x = (x - 1.) / (x + 1.); 79 | 1 80 | } 81 | } else if ix < 0x401c0000 { 82 | /* |x| < 2.4375 */ 83 | x = (x - 1.5) / (1. + 1.5 * x); 84 | 2 85 | } else { 86 | /* 2.4375 <= |x| < 2**26 */ 87 | x = -1. / x; 88 | 3 89 | } 90 | }; 91 | /* end of argument reduction */ 92 | z = x * x; 93 | let w = z * z; 94 | /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */ 95 | let s1 = z * (i!(A_T, 0) + w * (i!(A_T, 2) + w * i!(A_T, 4))); 96 | let s2 = w * (i!(A_T, 1) + w * i!(A_T, 3)); 97 | if id < 0 { 98 | return x - x * (s1 + s2); 99 | } 100 | let id = id as usize; 101 | let z = i!(ATAN_HI, id) - ((x * (s1 + s2) - i!(ATAN_LO, id)) - x); 102 | if sign { -z } else { z } 103 | } 104 | -------------------------------------------------------------------------------- /libm/src/math/atanh.rs: -------------------------------------------------------------------------------- 1 | use super::log1p; 2 | 3 | /* atanh(x) = log((1+x)/(1-x))/2 = log1p(2x/(1-x))/2 ~= x + x^3/3 + o(x^5) */ 4 | /// Inverse hyperbolic tangent (f64) 5 | /// 6 | /// Calculates the inverse hyperbolic tangent of `x`. 7 | /// Is defined as `log((1+x)/(1-x))/2 = log1p(2x/(1-x))/2`. 8 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 9 | pub fn atanh(x: f64) -> f64 { 10 | let u = x.to_bits(); 11 | let e = ((u >> 52) as usize) & 0x7ff; 12 | let sign = (u >> 63) != 0; 13 | 14 | /* |x| */ 15 | let mut y = f64::from_bits(u & 0x7fff_ffff_ffff_ffff); 16 | 17 | if e < 0x3ff - 1 { 18 | if e < 0x3ff - 32 { 19 | /* handle underflow */ 20 | if e == 0 { 21 | force_eval!(y as f32); 22 | } 23 | } else { 24 | /* |x| < 0.5, up to 1.7ulp error */ 25 | y = 0.5 * log1p(2.0 * y + 2.0 * y * y / (1.0 - y)); 26 | } 27 | } else { 28 | /* avoid overflow */ 29 | y = 0.5 * log1p(2.0 * (y / (1.0 - y))); 30 | } 31 | 32 | if sign { -y } else { y } 33 | } 34 | -------------------------------------------------------------------------------- /libm/src/math/atanhf.rs: -------------------------------------------------------------------------------- 1 | use super::log1pf; 2 | 3 | /* atanh(x) = log((1+x)/(1-x))/2 = log1p(2x/(1-x))/2 ~= x + x^3/3 + o(x^5) */ 4 | /// Inverse hyperbolic tangent (f32) 5 | /// 6 | /// Calculates the inverse hyperbolic tangent of `x`. 7 | /// Is defined as `log((1+x)/(1-x))/2 = log1p(2x/(1-x))/2`. 8 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 9 | pub fn atanhf(mut x: f32) -> f32 { 10 | let mut u = x.to_bits(); 11 | let sign = (u >> 31) != 0; 12 | 13 | /* |x| */ 14 | u &= 0x7fffffff; 15 | x = f32::from_bits(u); 16 | 17 | if u < 0x3f800000 - (1 << 23) { 18 | if u < 0x3f800000 - (32 << 23) { 19 | /* handle underflow */ 20 | if u < (1 << 23) { 21 | force_eval!(x * x); 22 | } 23 | } else { 24 | /* |x| < 0.5, up to 1.7ulp error */ 25 | x = 0.5 * log1pf(2.0 * x + 2.0 * x * x / (1.0 - x)); 26 | } 27 | } else { 28 | /* avoid overflow */ 29 | x = 0.5 * log1pf(2.0 * (x / (1.0 - x))); 30 | } 31 | 32 | if sign { -x } else { x } 33 | } 34 | -------------------------------------------------------------------------------- /libm/src/math/cbrtf.rs: -------------------------------------------------------------------------------- 1 | /* origin: FreeBSD /usr/src/lib/msun/src/s_cbrtf.c */ 2 | /* 3 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 4 | * Debugged and optimized by Bruce D. Evans. 5 | */ 6 | /* 7 | * ==================================================== 8 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 9 | * 10 | * Developed at SunPro, a Sun Microsystems, Inc. business. 11 | * Permission to use, copy, modify, and distribute this 12 | * software is freely granted, provided that this notice 13 | * is preserved. 14 | * ==================================================== 15 | */ 16 | /* cbrtf(x) 17 | * Return cube root of x 18 | */ 19 | 20 | use core::f32; 21 | 22 | const B1: u32 = 709958130; /* B1 = (127-127.0/3-0.03306235651)*2**23 */ 23 | const B2: u32 = 642849266; /* B2 = (127-127.0/3-24/3-0.03306235651)*2**23 */ 24 | 25 | /// Cube root (f32) 26 | /// 27 | /// Computes the cube root of the argument. 28 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 29 | pub fn cbrtf(x: f32) -> f32 { 30 | let x1p24 = f32::from_bits(0x4b800000); // 0x1p24f === 2 ^ 24 31 | 32 | let mut r: f64; 33 | let mut t: f64; 34 | let mut ui: u32 = x.to_bits(); 35 | let mut hx: u32 = ui & 0x7fffffff; 36 | 37 | if hx >= 0x7f800000 { 38 | /* cbrt(NaN,INF) is itself */ 39 | return x + x; 40 | } 41 | 42 | /* rough cbrt to 5 bits */ 43 | if hx < 0x00800000 { 44 | /* zero or subnormal? */ 45 | if hx == 0 { 46 | return x; /* cbrt(+-0) is itself */ 47 | } 48 | ui = (x * x1p24).to_bits(); 49 | hx = ui & 0x7fffffff; 50 | hx = hx / 3 + B2; 51 | } else { 52 | hx = hx / 3 + B1; 53 | } 54 | ui &= 0x80000000; 55 | ui |= hx; 56 | 57 | /* 58 | * First step Newton iteration (solving t*t-x/t == 0) to 16 bits. In 59 | * double precision so that its terms can be arranged for efficiency 60 | * without causing overflow or underflow. 61 | */ 62 | t = f32::from_bits(ui) as f64; 63 | r = t * t * t; 64 | t = t * (x as f64 + x as f64 + r) / (x as f64 + r + r); 65 | 66 | /* 67 | * Second step Newton iteration to 47 bits. In double precision for 68 | * efficiency and accuracy. 69 | */ 70 | r = t * t * t; 71 | t = t * (x as f64 + x as f64 + r) / (x as f64 + r + r); 72 | 73 | /* rounding to 24 bits is perfect in round-to-nearest mode */ 74 | t as f32 75 | } 76 | -------------------------------------------------------------------------------- /libm/src/math/ceil.rs: -------------------------------------------------------------------------------- 1 | /// Ceil (f16) 2 | /// 3 | /// Finds the nearest integer greater than or equal to `x`. 4 | #[cfg(f16_enabled)] 5 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 6 | pub fn ceilf16(x: f16) -> f16 { 7 | super::generic::ceil(x) 8 | } 9 | 10 | /// Ceil (f32) 11 | /// 12 | /// Finds the nearest integer greater than or equal to `x`. 13 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 14 | pub fn ceilf(x: f32) -> f32 { 15 | select_implementation! { 16 | name: ceilf, 17 | use_arch: all(target_arch = "wasm32", intrinsics_enabled), 18 | args: x, 19 | } 20 | 21 | super::generic::ceil(x) 22 | } 23 | 24 | /// Ceil (f64) 25 | /// 26 | /// Finds the nearest integer greater than or equal to `x`. 27 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 28 | pub fn ceil(x: f64) -> f64 { 29 | select_implementation! { 30 | name: ceil, 31 | use_arch: all(target_arch = "wasm32", intrinsics_enabled), 32 | use_arch_required: all(target_arch = "x86", not(target_feature = "sse2")), 33 | args: x, 34 | } 35 | 36 | super::generic::ceil(x) 37 | } 38 | 39 | /// Ceil (f128) 40 | /// 41 | /// Finds the nearest integer greater than or equal to `x`. 42 | #[cfg(f128_enabled)] 43 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 44 | pub fn ceilf128(x: f128) -> f128 { 45 | super::generic::ceil(x) 46 | } 47 | -------------------------------------------------------------------------------- /libm/src/math/copysign.rs: -------------------------------------------------------------------------------- 1 | /// Sign of Y, magnitude of X (f16) 2 | /// 3 | /// Constructs a number with the magnitude (absolute value) of its 4 | /// first argument, `x`, and the sign of its second argument, `y`. 5 | #[cfg(f16_enabled)] 6 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 7 | pub fn copysignf16(x: f16, y: f16) -> f16 { 8 | super::generic::copysign(x, y) 9 | } 10 | 11 | /// Sign of Y, magnitude of X (f32) 12 | /// 13 | /// Constructs a number with the magnitude (absolute value) of its 14 | /// first argument, `x`, and the sign of its second argument, `y`. 15 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 16 | pub fn copysignf(x: f32, y: f32) -> f32 { 17 | super::generic::copysign(x, y) 18 | } 19 | 20 | /// Sign of Y, magnitude of X (f64) 21 | /// 22 | /// Constructs a number with the magnitude (absolute value) of its 23 | /// first argument, `x`, and the sign of its second argument, `y`. 24 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 25 | pub fn copysign(x: f64, y: f64) -> f64 { 26 | super::generic::copysign(x, y) 27 | } 28 | 29 | /// Sign of Y, magnitude of X (f128) 30 | /// 31 | /// Constructs a number with the magnitude (absolute value) of its 32 | /// first argument, `x`, and the sign of its second argument, `y`. 33 | #[cfg(f128_enabled)] 34 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 35 | pub fn copysignf128(x: f128, y: f128) -> f128 { 36 | super::generic::copysign(x, y) 37 | } 38 | 39 | #[cfg(test)] 40 | mod tests { 41 | use super::*; 42 | use crate::support::Float; 43 | 44 | fn spec_test(f: impl Fn(F, F) -> F) { 45 | assert_biteq!(f(F::ZERO, F::ZERO), F::ZERO); 46 | assert_biteq!(f(F::NEG_ZERO, F::ZERO), F::ZERO); 47 | assert_biteq!(f(F::ZERO, F::NEG_ZERO), F::NEG_ZERO); 48 | assert_biteq!(f(F::NEG_ZERO, F::NEG_ZERO), F::NEG_ZERO); 49 | 50 | assert_biteq!(f(F::ONE, F::ONE), F::ONE); 51 | assert_biteq!(f(F::NEG_ONE, F::ONE), F::ONE); 52 | assert_biteq!(f(F::ONE, F::NEG_ONE), F::NEG_ONE); 53 | assert_biteq!(f(F::NEG_ONE, F::NEG_ONE), F::NEG_ONE); 54 | 55 | assert_biteq!(f(F::INFINITY, F::INFINITY), F::INFINITY); 56 | assert_biteq!(f(F::NEG_INFINITY, F::INFINITY), F::INFINITY); 57 | assert_biteq!(f(F::INFINITY, F::NEG_INFINITY), F::NEG_INFINITY); 58 | assert_biteq!(f(F::NEG_INFINITY, F::NEG_INFINITY), F::NEG_INFINITY); 59 | 60 | // Not required but we expect it 61 | assert_biteq!(f(F::NAN, F::NAN), F::NAN); 62 | assert_biteq!(f(F::NEG_NAN, F::NAN), F::NAN); 63 | assert_biteq!(f(F::NAN, F::NEG_NAN), F::NEG_NAN); 64 | assert_biteq!(f(F::NEG_NAN, F::NEG_NAN), F::NEG_NAN); 65 | } 66 | 67 | #[test] 68 | #[cfg(f16_enabled)] 69 | fn spec_tests_f16() { 70 | spec_test::(copysignf16); 71 | } 72 | 73 | #[test] 74 | fn spec_tests_f32() { 75 | spec_test::(copysignf); 76 | } 77 | 78 | #[test] 79 | fn spec_tests_f64() { 80 | spec_test::(copysign); 81 | } 82 | 83 | #[test] 84 | #[cfg(f128_enabled)] 85 | fn spec_tests_f128() { 86 | spec_test::(copysignf128); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /libm/src/math/copysignf.rs: -------------------------------------------------------------------------------- 1 | /// Sign of Y, magnitude of X (f32) 2 | /// 3 | /// Constructs a number with the magnitude (absolute value) of its 4 | /// first argument, `x`, and the sign of its second argument, `y`. 5 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 6 | pub fn copysignf(x: f32, y: f32) -> f32 { 7 | super::generic::copysign(x, y) 8 | } 9 | -------------------------------------------------------------------------------- /libm/src/math/copysignf128.rs: -------------------------------------------------------------------------------- 1 | /// Sign of Y, magnitude of X (f128) 2 | /// 3 | /// Constructs a number with the magnitude (absolute value) of its 4 | /// first argument, `x`, and the sign of its second argument, `y`. 5 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 6 | pub fn copysignf128(x: f128, y: f128) -> f128 { 7 | super::generic::copysign(x, y) 8 | } 9 | -------------------------------------------------------------------------------- /libm/src/math/copysignf16.rs: -------------------------------------------------------------------------------- 1 | /// Sign of Y, magnitude of X (f16) 2 | /// 3 | /// Constructs a number with the magnitude (absolute value) of its 4 | /// first argument, `x`, and the sign of its second argument, `y`. 5 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 6 | pub fn copysignf16(x: f16, y: f16) -> f16 { 7 | super::generic::copysign(x, y) 8 | } 9 | -------------------------------------------------------------------------------- /libm/src/math/cos.rs: -------------------------------------------------------------------------------- 1 | // origin: FreeBSD /usr/src/lib/msun/src/s_cos.c */ 2 | // 3 | // ==================================================== 4 | // Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 5 | // 6 | // Developed at SunPro, a Sun Microsystems, Inc. business. 7 | // Permission to use, copy, modify, and distribute this 8 | // software is freely granted, provided that this notice 9 | // is preserved. 10 | // ==================================================== 11 | 12 | use super::{k_cos, k_sin, rem_pio2}; 13 | 14 | // cos(x) 15 | // Return cosine function of x. 16 | // 17 | // kernel function: 18 | // k_sin ... sine function on [-pi/4,pi/4] 19 | // k_cos ... cosine function on [-pi/4,pi/4] 20 | // rem_pio2 ... argument reduction routine 21 | // 22 | // Method. 23 | // Let S,C and T denote the sin, cos and tan respectively on 24 | // [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 25 | // in [-pi/4 , +pi/4], and let n = k mod 4. 26 | // We have 27 | // 28 | // n sin(x) cos(x) tan(x) 29 | // ---------------------------------------------------------- 30 | // 0 S C T 31 | // 1 C -S -1/T 32 | // 2 -S -C T 33 | // 3 -C S -1/T 34 | // ---------------------------------------------------------- 35 | // 36 | // Special cases: 37 | // Let trig be any of sin, cos, or tan. 38 | // trig(+-INF) is NaN, with signals; 39 | // trig(NaN) is that NaN; 40 | // 41 | // Accuracy: 42 | // TRIG(x) returns trig(x) nearly rounded 43 | // 44 | 45 | /// The cosine of `x` (f64). 46 | /// 47 | /// `x` is specified in radians. 48 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 49 | pub fn cos(x: f64) -> f64 { 50 | let ix = (f64::to_bits(x) >> 32) as u32 & 0x7fffffff; 51 | 52 | /* |x| ~< pi/4 */ 53 | if ix <= 0x3fe921fb { 54 | if ix < 0x3e46a09e { 55 | /* if x < 2**-27 * sqrt(2) */ 56 | /* raise inexact if x != 0 */ 57 | if x as i32 == 0 { 58 | return 1.0; 59 | } 60 | } 61 | return k_cos(x, 0.0); 62 | } 63 | 64 | /* cos(Inf or NaN) is NaN */ 65 | if ix >= 0x7ff00000 { 66 | return x - x; 67 | } 68 | 69 | /* argument reduction needed */ 70 | let (n, y0, y1) = rem_pio2(x); 71 | match n & 3 { 72 | 0 => k_cos(y0, y1), 73 | 1 => -k_sin(y0, y1, 1), 74 | 2 => -k_cos(y0, y1), 75 | _ => k_sin(y0, y1, 1), 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /libm/src/math/cosf.rs: -------------------------------------------------------------------------------- 1 | /* origin: FreeBSD /usr/src/lib/msun/src/s_cosf.c */ 2 | /* 3 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 4 | * Optimized by Bruce D. Evans. 5 | */ 6 | /* 7 | * ==================================================== 8 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 9 | * 10 | * Developed at SunPro, a Sun Microsystems, Inc. business. 11 | * Permission to use, copy, modify, and distribute this 12 | * software is freely granted, provided that this notice 13 | * is preserved. 14 | * ==================================================== 15 | */ 16 | 17 | use core::f64::consts::FRAC_PI_2; 18 | 19 | use super::{k_cosf, k_sinf, rem_pio2f}; 20 | 21 | /* Small multiples of pi/2 rounded to double precision. */ 22 | const C1_PIO2: f64 = 1. * FRAC_PI_2; /* 0x3FF921FB, 0x54442D18 */ 23 | const C2_PIO2: f64 = 2. * FRAC_PI_2; /* 0x400921FB, 0x54442D18 */ 24 | const C3_PIO2: f64 = 3. * FRAC_PI_2; /* 0x4012D97C, 0x7F3321D2 */ 25 | const C4_PIO2: f64 = 4. * FRAC_PI_2; /* 0x401921FB, 0x54442D18 */ 26 | 27 | /// The cosine of `x` (f32). 28 | /// 29 | /// `x` is specified in radians. 30 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 31 | pub fn cosf(x: f32) -> f32 { 32 | let x64 = x as f64; 33 | 34 | let x1p120 = f32::from_bits(0x7b800000); // 0x1p120f === 2 ^ 120 35 | 36 | let mut ix = x.to_bits(); 37 | let sign = (ix >> 31) != 0; 38 | ix &= 0x7fffffff; 39 | 40 | if ix <= 0x3f490fda { 41 | /* |x| ~<= pi/4 */ 42 | if ix < 0x39800000 { 43 | /* |x| < 2**-12 */ 44 | /* raise inexact if x != 0 */ 45 | force_eval!(x + x1p120); 46 | return 1.; 47 | } 48 | return k_cosf(x64); 49 | } 50 | if ix <= 0x407b53d1 { 51 | /* |x| ~<= 5*pi/4 */ 52 | if ix > 0x4016cbe3 { 53 | /* |x| ~> 3*pi/4 */ 54 | return -k_cosf(if sign { x64 + C2_PIO2 } else { x64 - C2_PIO2 }); 55 | } else if sign { 56 | return k_sinf(x64 + C1_PIO2); 57 | } else { 58 | return k_sinf(C1_PIO2 - x64); 59 | } 60 | } 61 | if ix <= 0x40e231d5 { 62 | /* |x| ~<= 9*pi/4 */ 63 | if ix > 0x40afeddf { 64 | /* |x| ~> 7*pi/4 */ 65 | return k_cosf(if sign { x64 + C4_PIO2 } else { x64 - C4_PIO2 }); 66 | } else if sign { 67 | return k_sinf(-x64 - C3_PIO2); 68 | } else { 69 | return k_sinf(x64 - C3_PIO2); 70 | } 71 | } 72 | 73 | /* cos(Inf or NaN) is NaN */ 74 | if ix >= 0x7f800000 { 75 | return x - x; 76 | } 77 | 78 | /* general argument reduction needed */ 79 | let (n, y) = rem_pio2f(x); 80 | match n & 3 { 81 | 0 => k_cosf(y), 82 | 1 => k_sinf(-y), 83 | 2 => -k_cosf(y), 84 | _ => k_sinf(y), 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /libm/src/math/cosh.rs: -------------------------------------------------------------------------------- 1 | use super::{exp, expm1, k_expo2}; 2 | 3 | /// Hyperbolic cosine (f64) 4 | /// 5 | /// Computes the hyperbolic cosine of the argument x. 6 | /// Is defined as `(exp(x) + exp(-x))/2` 7 | /// Angles are specified in radians. 8 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 9 | pub fn cosh(mut x: f64) -> f64 { 10 | /* |x| */ 11 | let mut ix = x.to_bits(); 12 | ix &= 0x7fffffffffffffff; 13 | x = f64::from_bits(ix); 14 | let w = ix >> 32; 15 | 16 | /* |x| < log(2) */ 17 | if w < 0x3fe62e42 { 18 | if w < 0x3ff00000 - (26 << 20) { 19 | let x1p120 = f64::from_bits(0x4770000000000000); 20 | force_eval!(x + x1p120); 21 | return 1.; 22 | } 23 | let t = expm1(x); // exponential minus 1 24 | return 1. + t * t / (2. * (1. + t)); 25 | } 26 | 27 | /* |x| < log(DBL_MAX) */ 28 | if w < 0x40862e42 { 29 | let t = exp(x); 30 | /* note: if x>log(0x1p26) then the 1/t is not needed */ 31 | return 0.5 * (t + 1. / t); 32 | } 33 | 34 | /* |x| > log(DBL_MAX) or nan */ 35 | k_expo2(x) 36 | } 37 | -------------------------------------------------------------------------------- /libm/src/math/coshf.rs: -------------------------------------------------------------------------------- 1 | use super::{expf, expm1f, k_expo2f}; 2 | 3 | /// Hyperbolic cosine (f64) 4 | /// 5 | /// Computes the hyperbolic cosine of the argument x. 6 | /// Is defined as `(exp(x) + exp(-x))/2` 7 | /// Angles are specified in radians. 8 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 9 | pub fn coshf(mut x: f32) -> f32 { 10 | let x1p120 = f32::from_bits(0x7b800000); // 0x1p120f === 2 ^ 120 11 | 12 | /* |x| */ 13 | let mut ix = x.to_bits(); 14 | ix &= 0x7fffffff; 15 | x = f32::from_bits(ix); 16 | let w = ix; 17 | 18 | /* |x| < log(2) */ 19 | if w < 0x3f317217 { 20 | if w < (0x3f800000 - (12 << 23)) { 21 | force_eval!(x + x1p120); 22 | return 1.; 23 | } 24 | let t = expm1f(x); 25 | return 1. + t * t / (2. * (1. + t)); 26 | } 27 | 28 | /* |x| < log(FLT_MAX) */ 29 | if w < 0x42b17217 { 30 | let t = expf(x); 31 | return 0.5 * (t + 1. / t); 32 | } 33 | 34 | /* |x| > log(FLT_MAX) or nan */ 35 | k_expo2f(x) 36 | } 37 | -------------------------------------------------------------------------------- /libm/src/math/exp10.rs: -------------------------------------------------------------------------------- 1 | use super::{exp2, modf, pow}; 2 | 3 | const LN10: f64 = 3.32192809488736234787031942948939; 4 | const P10: &[f64] = &[ 5 | 1e-15, 1e-14, 1e-13, 1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 6 | 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 7 | ]; 8 | 9 | /// Calculates 10 raised to the power of `x` (f64). 10 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 11 | pub fn exp10(x: f64) -> f64 { 12 | let (mut y, n) = modf(x); 13 | let u: u64 = n.to_bits(); 14 | /* fabs(n) < 16 without raising invalid on nan */ 15 | if ((u >> 52) & 0x7ff) < 0x3ff + 4 { 16 | if y == 0.0 { 17 | return i!(P10, ((n as isize) + 15) as usize); 18 | } 19 | y = exp2(LN10 * y); 20 | return y * i!(P10, ((n as isize) + 15) as usize); 21 | } 22 | return pow(10.0, x); 23 | } 24 | -------------------------------------------------------------------------------- /libm/src/math/exp10f.rs: -------------------------------------------------------------------------------- 1 | use super::{exp2, exp2f, modff}; 2 | 3 | const LN10_F32: f32 = 3.32192809488736234787031942948939; 4 | const LN10_F64: f64 = 3.32192809488736234787031942948939; 5 | const P10: &[f32] = 6 | &[1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7]; 7 | 8 | /// Calculates 10 raised to the power of `x` (f32). 9 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 10 | pub fn exp10f(x: f32) -> f32 { 11 | let (mut y, n) = modff(x); 12 | let u = n.to_bits(); 13 | /* fabsf(n) < 8 without raising invalid on nan */ 14 | if ((u >> 23) & 0xff) < 0x7f + 3 { 15 | if y == 0.0 { 16 | return i!(P10, ((n as isize) + 7) as usize); 17 | } 18 | y = exp2f(LN10_F32 * y); 19 | return y * i!(P10, ((n as isize) + 7) as usize); 20 | } 21 | return exp2(LN10_F64 * (x as f64)) as f32; 22 | } 23 | -------------------------------------------------------------------------------- /libm/src/math/expf.rs: -------------------------------------------------------------------------------- 1 | /* origin: FreeBSD /usr/src/lib/msun/src/e_expf.c */ 2 | /* 3 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 4 | */ 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | use super::scalbnf; 17 | 18 | const HALF: [f32; 2] = [0.5, -0.5]; 19 | const LN2_HI: f32 = 6.9314575195e-01; /* 0x3f317200 */ 20 | const LN2_LO: f32 = 1.4286067653e-06; /* 0x35bfbe8e */ 21 | const INV_LN2: f32 = 1.4426950216e+00; /* 0x3fb8aa3b */ 22 | /* 23 | * Domain [-0.34568, 0.34568], range ~[-4.278e-9, 4.447e-9]: 24 | * |x*(exp(x)+1)/(exp(x)-1) - p(x)| < 2**-27.74 25 | */ 26 | const P1: f32 = 1.6666625440e-1; /* 0xaaaa8f.0p-26 */ 27 | const P2: f32 = -2.7667332906e-3; /* -0xb55215.0p-32 */ 28 | 29 | /// Exponential, base *e* (f32) 30 | /// 31 | /// Calculate the exponential of `x`, that is, *e* raised to the power `x` 32 | /// (where *e* is the base of the natural system of logarithms, approximately 2.71828). 33 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 34 | pub fn expf(mut x: f32) -> f32 { 35 | let x1p127 = f32::from_bits(0x7f000000); // 0x1p127f === 2 ^ 127 36 | let x1p_126 = f32::from_bits(0x800000); // 0x1p-126f === 2 ^ -126 /*original 0x1p-149f ??????????? */ 37 | let mut hx = x.to_bits(); 38 | let sign = (hx >> 31) as i32; /* sign bit of x */ 39 | let signb: bool = sign != 0; 40 | hx &= 0x7fffffff; /* high word of |x| */ 41 | 42 | /* special cases */ 43 | if hx >= 0x42aeac50 { 44 | /* if |x| >= -87.33655f or NaN */ 45 | if hx > 0x7f800000 { 46 | /* NaN */ 47 | return x; 48 | } 49 | if (hx >= 0x42b17218) && (!signb) { 50 | /* x >= 88.722839f */ 51 | /* overflow */ 52 | x *= x1p127; 53 | return x; 54 | } 55 | if signb { 56 | /* underflow */ 57 | force_eval!(-x1p_126 / x); 58 | if hx >= 0x42cff1b5 { 59 | /* x <= -103.972084f */ 60 | return 0.; 61 | } 62 | } 63 | } 64 | 65 | /* argument reduction */ 66 | let k: i32; 67 | let hi: f32; 68 | let lo: f32; 69 | if hx > 0x3eb17218 { 70 | /* if |x| > 0.5 ln2 */ 71 | if hx > 0x3f851592 { 72 | /* if |x| > 1.5 ln2 */ 73 | k = (INV_LN2 * x + i!(HALF, sign as usize)) as i32; 74 | } else { 75 | k = 1 - sign - sign; 76 | } 77 | let kf = k as f32; 78 | hi = x - kf * LN2_HI; /* k*ln2hi is exact here */ 79 | lo = kf * LN2_LO; 80 | x = hi - lo; 81 | } else if hx > 0x39000000 { 82 | /* |x| > 2**-14 */ 83 | k = 0; 84 | hi = x; 85 | lo = 0.; 86 | } else { 87 | /* raise inexact */ 88 | force_eval!(x1p127 + x); 89 | return 1. + x; 90 | } 91 | 92 | /* x is now in primary range */ 93 | let xx = x * x; 94 | let c = x - xx * (P1 + xx * P2); 95 | let y = 1. + (x * c / (2. - c) - lo + hi); 96 | if k == 0 { y } else { scalbnf(y, k) } 97 | } 98 | -------------------------------------------------------------------------------- /libm/src/math/expo2.rs: -------------------------------------------------------------------------------- 1 | use super::{combine_words, exp}; 2 | 3 | /* exp(x)/2 for x >= log(DBL_MAX), slightly better than 0.5*exp(x/2)*exp(x/2) */ 4 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 5 | pub(crate) fn expo2(x: f64) -> f64 { 6 | /* k is such that k*ln2 has minimal relative error and x - kln2 > log(DBL_MIN) */ 7 | const K: i32 = 2043; 8 | let kln2 = f64::from_bits(0x40962066151add8b); 9 | 10 | /* note that k is odd and scale*scale overflows */ 11 | let scale = combine_words(((0x3ff + K / 2) as u32) << 20, 0); 12 | /* exp(x - k ln2) * 2**(k-1) */ 13 | exp(x - kln2) * scale * scale 14 | } 15 | -------------------------------------------------------------------------------- /libm/src/math/fabs.rs: -------------------------------------------------------------------------------- 1 | /// Absolute value (magnitude) (f16) 2 | /// 3 | /// Calculates the absolute value (magnitude) of the argument `x`, 4 | /// by direct manipulation of the bit representation of `x`. 5 | #[cfg(f16_enabled)] 6 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 7 | pub fn fabsf16(x: f16) -> f16 { 8 | super::generic::fabs(x) 9 | } 10 | 11 | /// Absolute value (magnitude) (f32) 12 | /// 13 | /// Calculates the absolute value (magnitude) of the argument `x`, 14 | /// by direct manipulation of the bit representation of `x`. 15 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 16 | pub fn fabsf(x: f32) -> f32 { 17 | select_implementation! { 18 | name: fabsf, 19 | use_arch: all(target_arch = "wasm32", intrinsics_enabled), 20 | args: x, 21 | } 22 | 23 | super::generic::fabs(x) 24 | } 25 | 26 | /// Absolute value (magnitude) (f64) 27 | /// 28 | /// Calculates the absolute value (magnitude) of the argument `x`, 29 | /// by direct manipulation of the bit representation of `x`. 30 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 31 | pub fn fabs(x: f64) -> f64 { 32 | select_implementation! { 33 | name: fabs, 34 | use_arch: all(target_arch = "wasm32", intrinsics_enabled), 35 | args: x, 36 | } 37 | 38 | super::generic::fabs(x) 39 | } 40 | 41 | /// Absolute value (magnitude) (f128) 42 | /// 43 | /// Calculates the absolute value (magnitude) of the argument `x`, 44 | /// by direct manipulation of the bit representation of `x`. 45 | #[cfg(f128_enabled)] 46 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 47 | pub fn fabsf128(x: f128) -> f128 { 48 | super::generic::fabs(x) 49 | } 50 | 51 | #[cfg(test)] 52 | mod tests { 53 | use super::*; 54 | use crate::support::Float; 55 | 56 | /// Based on https://en.cppreference.com/w/cpp/numeric/math/fabs 57 | fn spec_test(f: impl Fn(F) -> F) { 58 | assert_biteq!(f(F::ZERO), F::ZERO); 59 | assert_biteq!(f(F::NEG_ZERO), F::ZERO); 60 | assert_biteq!(f(F::INFINITY), F::INFINITY); 61 | assert_biteq!(f(F::NEG_INFINITY), F::INFINITY); 62 | assert!(f(F::NAN).is_nan()); 63 | 64 | // Not spec rewquired but we expect it 65 | assert!(f(F::NAN).is_sign_positive()); 66 | assert!(f(F::from_bits(F::NAN.to_bits() | F::SIGN_MASK)).is_sign_positive()); 67 | } 68 | 69 | #[test] 70 | #[cfg(f16_enabled)] 71 | fn sanity_check_f16() { 72 | assert_eq!(fabsf16(-1.0f16), 1.0); 73 | assert_eq!(fabsf16(2.8f16), 2.8); 74 | } 75 | 76 | #[test] 77 | #[cfg(f16_enabled)] 78 | fn spec_tests_f16() { 79 | spec_test::(fabsf16); 80 | } 81 | 82 | #[test] 83 | fn sanity_check_f32() { 84 | assert_eq!(fabsf(-1.0f32), 1.0); 85 | assert_eq!(fabsf(2.8f32), 2.8); 86 | } 87 | 88 | #[test] 89 | fn spec_tests_f32() { 90 | spec_test::(fabsf); 91 | } 92 | 93 | #[test] 94 | fn sanity_check_f64() { 95 | assert_eq!(fabs(-1.0f64), 1.0); 96 | assert_eq!(fabs(2.8f64), 2.8); 97 | } 98 | 99 | #[test] 100 | fn spec_tests_f64() { 101 | spec_test::(fabs); 102 | } 103 | 104 | #[test] 105 | #[cfg(f128_enabled)] 106 | fn sanity_check_f128() { 107 | assert_eq!(fabsf128(-1.0f128), 1.0); 108 | assert_eq!(fabsf128(2.8f128), 2.8); 109 | } 110 | 111 | #[test] 112 | #[cfg(f128_enabled)] 113 | fn spec_tests_f128() { 114 | spec_test::(fabsf128); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /libm/src/math/fabsf.rs: -------------------------------------------------------------------------------- 1 | /// Absolute value (magnitude) (f32) 2 | /// 3 | /// Calculates the absolute value (magnitude) of the argument `x`, 4 | /// by direct manipulation of the bit representation of `x`. 5 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 6 | pub fn fabsf(x: f32) -> f32 { 7 | select_implementation! { 8 | name: fabsf, 9 | use_arch: all(target_arch = "wasm32", intrinsics_enabled), 10 | args: x, 11 | } 12 | 13 | super::generic::fabs(x) 14 | } 15 | 16 | // PowerPC tests are failing on LLVM 13: https://github.com/rust-lang/rust/issues/88520 17 | #[cfg(not(target_arch = "powerpc64"))] 18 | #[cfg(test)] 19 | mod tests { 20 | use super::*; 21 | 22 | #[test] 23 | fn sanity_check() { 24 | assert_eq!(fabsf(-1.0), 1.0); 25 | assert_eq!(fabsf(2.8), 2.8); 26 | } 27 | 28 | /// The spec: https://en.cppreference.com/w/cpp/numeric/math/fabs 29 | #[test] 30 | fn spec_tests() { 31 | assert!(fabsf(f32::NAN).is_nan()); 32 | for f in [0.0, -0.0].iter().copied() { 33 | assert_eq!(fabsf(f), 0.0); 34 | } 35 | for f in [f32::INFINITY, f32::NEG_INFINITY].iter().copied() { 36 | assert_eq!(fabsf(f), f32::INFINITY); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /libm/src/math/fabsf128.rs: -------------------------------------------------------------------------------- 1 | /// Absolute value (magnitude) (f128) 2 | /// 3 | /// Calculates the absolute value (magnitude) of the argument `x`, 4 | /// by direct manipulation of the bit representation of `x`. 5 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 6 | pub fn fabsf128(x: f128) -> f128 { 7 | super::generic::fabs(x) 8 | } 9 | 10 | #[cfg(test)] 11 | mod tests { 12 | use super::*; 13 | 14 | #[test] 15 | fn sanity_check() { 16 | assert_eq!(fabsf128(-1.0), 1.0); 17 | assert_eq!(fabsf128(2.8), 2.8); 18 | } 19 | 20 | /// The spec: https://en.cppreference.com/w/cpp/numeric/math/fabs 21 | #[test] 22 | fn spec_tests() { 23 | assert!(fabsf128(f128::NAN).is_nan()); 24 | for f in [0.0, -0.0].iter().copied() { 25 | assert_eq!(fabsf128(f), 0.0); 26 | } 27 | for f in [f128::INFINITY, f128::NEG_INFINITY].iter().copied() { 28 | assert_eq!(fabsf128(f), f128::INFINITY); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /libm/src/math/fabsf16.rs: -------------------------------------------------------------------------------- 1 | /// Absolute value (magnitude) (f16) 2 | /// 3 | /// Calculates the absolute value (magnitude) of the argument `x`, 4 | /// by direct manipulation of the bit representation of `x`. 5 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 6 | pub fn fabsf16(x: f16) -> f16 { 7 | super::generic::fabs(x) 8 | } 9 | 10 | #[cfg(test)] 11 | mod tests { 12 | use super::*; 13 | 14 | #[test] 15 | fn sanity_check() { 16 | assert_eq!(fabsf16(-1.0), 1.0); 17 | assert_eq!(fabsf16(2.8), 2.8); 18 | } 19 | 20 | /// The spec: https://en.cppreference.com/w/cpp/numeric/math/fabs 21 | #[test] 22 | fn spec_tests() { 23 | assert!(fabsf16(f16::NAN).is_nan()); 24 | for f in [0.0, -0.0].iter().copied() { 25 | assert_eq!(fabsf16(f), 0.0); 26 | } 27 | for f in [f16::INFINITY, f16::NEG_INFINITY].iter().copied() { 28 | assert_eq!(fabsf16(f), f16::INFINITY); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /libm/src/math/fdim.rs: -------------------------------------------------------------------------------- 1 | /// Positive difference (f16) 2 | /// 3 | /// Determines the positive difference between arguments, returning: 4 | /// * x - y if x > y, or 5 | /// * +0 if x <= y, or 6 | /// * NAN if either argument is NAN. 7 | /// 8 | /// A range error may occur. 9 | #[cfg(f16_enabled)] 10 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 11 | pub fn fdimf16(x: f16, y: f16) -> f16 { 12 | super::generic::fdim(x, y) 13 | } 14 | 15 | /// Positive difference (f32) 16 | /// 17 | /// Determines the positive difference between arguments, returning: 18 | /// * x - y if x > y, or 19 | /// * +0 if x <= y, or 20 | /// * NAN if either argument is NAN. 21 | /// 22 | /// A range error may occur. 23 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 24 | pub fn fdimf(x: f32, y: f32) -> f32 { 25 | super::generic::fdim(x, y) 26 | } 27 | 28 | /// Positive difference (f64) 29 | /// 30 | /// Determines the positive difference between arguments, returning: 31 | /// * x - y if x > y, or 32 | /// * +0 if x <= y, or 33 | /// * NAN if either argument is NAN. 34 | /// 35 | /// A range error may occur. 36 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 37 | pub fn fdim(x: f64, y: f64) -> f64 { 38 | super::generic::fdim(x, y) 39 | } 40 | 41 | /// Positive difference (f128) 42 | /// 43 | /// Determines the positive difference between arguments, returning: 44 | /// * x - y if x > y, or 45 | /// * +0 if x <= y, or 46 | /// * NAN if either argument is NAN. 47 | /// 48 | /// A range error may occur. 49 | #[cfg(f128_enabled)] 50 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 51 | pub fn fdimf128(x: f128, y: f128) -> f128 { 52 | super::generic::fdim(x, y) 53 | } 54 | -------------------------------------------------------------------------------- /libm/src/math/fdimf.rs: -------------------------------------------------------------------------------- 1 | /// Positive difference (f32) 2 | /// 3 | /// Determines the positive difference between arguments, returning: 4 | /// * x - y if x > y, or 5 | /// * +0 if x <= y, or 6 | /// * NAN if either argument is NAN. 7 | /// 8 | /// A range error may occur. 9 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 10 | pub fn fdimf(x: f32, y: f32) -> f32 { 11 | super::generic::fdim(x, y) 12 | } 13 | -------------------------------------------------------------------------------- /libm/src/math/fdimf128.rs: -------------------------------------------------------------------------------- 1 | /// Positive difference (f128) 2 | /// 3 | /// Determines the positive difference between arguments, returning: 4 | /// * x - y if x > y, or 5 | /// * +0 if x <= y, or 6 | /// * NAN if either argument is NAN. 7 | /// 8 | /// A range error may occur. 9 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 10 | pub fn fdimf128(x: f128, y: f128) -> f128 { 11 | super::generic::fdim(x, y) 12 | } 13 | -------------------------------------------------------------------------------- /libm/src/math/fdimf16.rs: -------------------------------------------------------------------------------- 1 | /// Positive difference (f16) 2 | /// 3 | /// Determines the positive difference between arguments, returning: 4 | /// * x - y if x > y, or 5 | /// * +0 if x <= y, or 6 | /// * NAN if either argument is NAN. 7 | /// 8 | /// A range error may occur. 9 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 10 | pub fn fdimf16(x: f16, y: f16) -> f16 { 11 | super::generic::fdim(x, y) 12 | } 13 | -------------------------------------------------------------------------------- /libm/src/math/floor.rs: -------------------------------------------------------------------------------- 1 | /// Floor (f16) 2 | /// 3 | /// Finds the nearest integer less than or equal to `x`. 4 | #[cfg(f16_enabled)] 5 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 6 | pub fn floorf16(x: f16) -> f16 { 7 | return super::generic::floor(x); 8 | } 9 | 10 | /// Floor (f64) 11 | /// 12 | /// Finds the nearest integer less than or equal to `x`. 13 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 14 | pub fn floor(x: f64) -> f64 { 15 | select_implementation! { 16 | name: floor, 17 | use_arch: all(target_arch = "wasm32", intrinsics_enabled), 18 | use_arch_required: all(target_arch = "x86", not(target_feature = "sse2")), 19 | args: x, 20 | } 21 | 22 | return super::generic::floor(x); 23 | } 24 | 25 | /// Floor (f32) 26 | /// 27 | /// Finds the nearest integer less than or equal to `x`. 28 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 29 | pub fn floorf(x: f32) -> f32 { 30 | select_implementation! { 31 | name: floorf, 32 | use_arch: all(target_arch = "wasm32", intrinsics_enabled), 33 | args: x, 34 | } 35 | 36 | return super::generic::floor(x); 37 | } 38 | 39 | /// Floor (f128) 40 | /// 41 | /// Finds the nearest integer less than or equal to `x`. 42 | #[cfg(f128_enabled)] 43 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 44 | pub fn floorf128(x: f128) -> f128 { 45 | return super::generic::floor(x); 46 | } 47 | -------------------------------------------------------------------------------- /libm/src/math/floorf.rs: -------------------------------------------------------------------------------- 1 | /// Floor (f32) 2 | /// 3 | /// Finds the nearest integer less than or equal to `x`. 4 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 5 | pub fn floorf(x: f32) -> f32 { 6 | select_implementation! { 7 | name: floorf, 8 | use_arch: all(target_arch = "wasm32", intrinsics_enabled), 9 | args: x, 10 | } 11 | 12 | return super::generic::floor(x); 13 | } 14 | -------------------------------------------------------------------------------- /libm/src/math/floorf128.rs: -------------------------------------------------------------------------------- 1 | /// Floor (f128) 2 | /// 3 | /// Finds the nearest integer less than or equal to `x`. 4 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 5 | pub fn floorf128(x: f128) -> f128 { 6 | return super::generic::floor(x); 7 | } 8 | -------------------------------------------------------------------------------- /libm/src/math/floorf16.rs: -------------------------------------------------------------------------------- 1 | /// Floor (f16) 2 | /// 3 | /// Finds the nearest integer less than or equal to `x`. 4 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 5 | pub fn floorf16(x: f16) -> f16 { 6 | return super::generic::floor(x); 7 | } 8 | -------------------------------------------------------------------------------- /libm/src/math/fma_wide.rs: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* origin: musl src/math/fmaf.c. Ported to generic Rust algorithm in 2025, TG. */ 3 | 4 | use super::support::{FpResult, IntTy, Round, Status}; 5 | use super::{CastFrom, CastInto, DFloat, Float, HFloat, MinInt}; 6 | 7 | // Placeholder so we can have `fmaf16` in the `Float` trait. 8 | #[allow(unused)] 9 | #[cfg(f16_enabled)] 10 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 11 | pub(crate) fn fmaf16(_x: f16, _y: f16, _z: f16) -> f16 { 12 | unimplemented!() 13 | } 14 | 15 | /// Floating multiply add (f32) 16 | /// 17 | /// Computes `(x*y)+z`, rounded as one ternary operation (i.e. calculated with infinite precision). 18 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 19 | pub fn fmaf(x: f32, y: f32, z: f32) -> f32 { 20 | select_implementation! { 21 | name: fmaf, 22 | use_arch: all(target_arch = "aarch64", target_feature = "neon"), 23 | args: x, y, z, 24 | } 25 | 26 | fma_wide_round(x, y, z, Round::Nearest).val 27 | } 28 | 29 | /// Fma implementation when a hardware-backed larger float type is available. For `f32` and `f64`, 30 | /// `f64` has enough precision to represent the `f32` in its entirety, except for double rounding. 31 | #[inline] 32 | pub fn fma_wide_round(x: F, y: F, z: F, round: Round) -> FpResult 33 | where 34 | F: Float + HFloat, 35 | B: Float + DFloat, 36 | B::Int: CastInto, 37 | i32: CastFrom, 38 | { 39 | let one = IntTy::::ONE; 40 | 41 | let xy: B = x.widen() * y.widen(); 42 | let mut result: B = xy + z.widen(); 43 | let mut ui: B::Int = result.to_bits(); 44 | let re = result.ex(); 45 | let zb: B = z.widen(); 46 | 47 | let prec_diff = B::SIG_BITS - F::SIG_BITS; 48 | let excess_prec = ui & ((one << prec_diff) - one); 49 | let halfway = one << (prec_diff - 1); 50 | 51 | // Common case: the larger precision is fine if... 52 | // This is not a halfway case 53 | if excess_prec != halfway 54 | // Or the result is NaN 55 | || re == B::EXP_SAT 56 | // Or the result is exact 57 | || (result - xy == zb && result - zb == xy) 58 | // Or the mode is something other than round to nearest 59 | || round != Round::Nearest 60 | { 61 | let min_inexact_exp = (B::EXP_BIAS as i32 + F::EXP_MIN_SUBNORM) as u32; 62 | let max_inexact_exp = (B::EXP_BIAS as i32 + F::EXP_MIN) as u32; 63 | 64 | let mut status = Status::OK; 65 | 66 | if (min_inexact_exp..max_inexact_exp).contains(&re) && status.inexact() { 67 | // This branch is never hit; requires previous operations to set a status 68 | status.set_inexact(false); 69 | 70 | result = xy + z.widen(); 71 | if status.inexact() { 72 | status.set_underflow(true); 73 | } else { 74 | status.set_inexact(true); 75 | } 76 | } 77 | 78 | return FpResult { val: result.narrow(), status }; 79 | } 80 | 81 | let neg = ui >> (B::BITS - 1) != IntTy::::ZERO; 82 | let err = if neg == (zb > xy) { xy - result + zb } else { zb - result + xy }; 83 | if neg == (err < B::ZERO) { 84 | ui += one; 85 | } else { 86 | ui -= one; 87 | } 88 | 89 | FpResult::ok(B::from_bits(ui).narrow()) 90 | } 91 | 92 | #[cfg(test)] 93 | mod tests { 94 | use super::*; 95 | 96 | #[test] 97 | fn issue_263() { 98 | let a = f32::from_bits(1266679807); 99 | let b = f32::from_bits(1300234242); 100 | let c = f32::from_bits(1115553792); 101 | let expected = f32::from_bits(1501560833); 102 | assert_eq!(fmaf(a, b, c), expected); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /libm/src/math/fmod.rs: -------------------------------------------------------------------------------- 1 | /// Calculate the remainder of `x / y`, the precise result of `x - trunc(x / y) * y`. 2 | #[cfg(f16_enabled)] 3 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 4 | pub fn fmodf16(x: f16, y: f16) -> f16 { 5 | super::generic::fmod(x, y) 6 | } 7 | 8 | /// Calculate the remainder of `x / y`, the precise result of `x - trunc(x / y) * y`. 9 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 10 | pub fn fmodf(x: f32, y: f32) -> f32 { 11 | super::generic::fmod(x, y) 12 | } 13 | 14 | /// Calculate the remainder of `x / y`, the precise result of `x - trunc(x / y) * y`. 15 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 16 | pub fn fmod(x: f64, y: f64) -> f64 { 17 | super::generic::fmod(x, y) 18 | } 19 | 20 | /// Calculate the remainder of `x / y`, the precise result of `x - trunc(x / y) * y`. 21 | #[cfg(f128_enabled)] 22 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 23 | pub fn fmodf128(x: f128, y: f128) -> f128 { 24 | super::generic::fmod(x, y) 25 | } 26 | -------------------------------------------------------------------------------- /libm/src/math/fmodf.rs: -------------------------------------------------------------------------------- 1 | /// Calculate the remainder of `x / y`, the precise result of `x - trunc(x / y) * y`. 2 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 3 | pub fn fmodf(x: f32, y: f32) -> f32 { 4 | super::generic::fmod(x, y) 5 | } 6 | -------------------------------------------------------------------------------- /libm/src/math/fmodf128.rs: -------------------------------------------------------------------------------- 1 | /// Calculate the remainder of `x / y`, the precise result of `x - trunc(x / y) * y`. 2 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 3 | pub fn fmodf128(x: f128, y: f128) -> f128 { 4 | super::generic::fmod(x, y) 5 | } 6 | -------------------------------------------------------------------------------- /libm/src/math/fmodf16.rs: -------------------------------------------------------------------------------- 1 | /// Calculate the remainder of `x / y`, the precise result of `x - trunc(x / y) * y`. 2 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 3 | pub fn fmodf16(x: f16, y: f16) -> f16 { 4 | super::generic::fmod(x, y) 5 | } 6 | -------------------------------------------------------------------------------- /libm/src/math/frexp.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 2 | pub fn frexp(x: f64) -> (f64, i32) { 3 | let mut y = x.to_bits(); 4 | let ee = ((y >> 52) & 0x7ff) as i32; 5 | 6 | if ee == 0 { 7 | if x != 0.0 { 8 | let x1p64 = f64::from_bits(0x43f0000000000000); 9 | let (x, e) = frexp(x * x1p64); 10 | return (x, e - 64); 11 | } 12 | return (x, 0); 13 | } else if ee == 0x7ff { 14 | return (x, 0); 15 | } 16 | 17 | let e = ee - 0x3fe; 18 | y &= 0x800fffffffffffff; 19 | y |= 0x3fe0000000000000; 20 | return (f64::from_bits(y), e); 21 | } 22 | -------------------------------------------------------------------------------- /libm/src/math/frexpf.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 2 | pub fn frexpf(x: f32) -> (f32, i32) { 3 | let mut y = x.to_bits(); 4 | let ee: i32 = ((y >> 23) & 0xff) as i32; 5 | 6 | if ee == 0 { 7 | if x != 0.0 { 8 | let x1p64 = f32::from_bits(0x5f800000); 9 | let (x, e) = frexpf(x * x1p64); 10 | return (x, e - 64); 11 | } else { 12 | return (x, 0); 13 | } 14 | } else if ee == 0xff { 15 | return (x, 0); 16 | } 17 | 18 | let e = ee - 0x7e; 19 | y &= 0x807fffff; 20 | y |= 0x3f000000; 21 | (f32::from_bits(y), e) 22 | } 23 | -------------------------------------------------------------------------------- /libm/src/math/generic/copysign.rs: -------------------------------------------------------------------------------- 1 | use super::super::Float; 2 | 3 | /// Copy the sign of `y` to `x`. 4 | #[inline] 5 | pub fn copysign(x: F, y: F) -> F { 6 | let mut ux = x.to_bits(); 7 | let uy = y.to_bits(); 8 | ux &= !F::SIGN_MASK; 9 | ux |= uy & F::SIGN_MASK; 10 | F::from_bits(ux) 11 | } 12 | -------------------------------------------------------------------------------- /libm/src/math/generic/fabs.rs: -------------------------------------------------------------------------------- 1 | use super::super::Float; 2 | 3 | /// Absolute value. 4 | #[inline] 5 | pub fn fabs(x: F) -> F { 6 | let abs_mask = !F::SIGN_MASK; 7 | F::from_bits(x.to_bits() & abs_mask) 8 | } 9 | -------------------------------------------------------------------------------- /libm/src/math/generic/fdim.rs: -------------------------------------------------------------------------------- 1 | use super::super::Float; 2 | 3 | #[inline] 4 | pub fn fdim(x: F, y: F) -> F { 5 | if x <= y { F::ZERO } else { x - y } 6 | } 7 | -------------------------------------------------------------------------------- /libm/src/math/generic/fmax.rs: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT OR Apache-2.0 */ 2 | //! IEEE 754-2011 `maxNum`. This has been superseded by IEEE 754-2019 `maximumNumber`. 3 | //! 4 | //! Per the spec, returns the canonicalized result of: 5 | //! - `x` if `x > y` 6 | //! - `y` if `y > x` 7 | //! - The other number if one is NaN 8 | //! - Otherwise, either `x` or `y`, canonicalized 9 | //! - -0.0 and +0.0 may be disregarded (unlike newer operations) 10 | //! 11 | //! Excluded from our implementation is sNaN handling. 12 | //! 13 | //! More on the differences: [link]. 14 | //! 15 | //! [link]: https://grouper.ieee.org/groups/msc/ANSI_IEEE-Std-754-2019/background/minNum_maxNum_Removal_Demotion_v3.pdf 16 | 17 | use super::super::Float; 18 | 19 | #[inline] 20 | pub fn fmax(x: F, y: F) -> F { 21 | let res = if x.is_nan() || x < y { y } else { x }; 22 | // Canonicalize 23 | res * F::ONE 24 | } 25 | -------------------------------------------------------------------------------- /libm/src/math/generic/fmaximum.rs: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT OR Apache-2.0 */ 2 | //! IEEE 754-2019 `maximum`. 3 | //! 4 | //! Per the spec, returns the canonicalized result of: 5 | //! - `x` if `x > y` 6 | //! - `y` if `y > x` 7 | //! - qNaN if either operation is NaN 8 | //! - Logic following +0.0 > -0.0 9 | //! 10 | //! Excluded from our implementation is sNaN handling. 11 | 12 | use super::super::Float; 13 | 14 | #[inline] 15 | pub fn fmaximum(x: F, y: F) -> F { 16 | let res = if x.is_nan() { 17 | x 18 | } else if y.is_nan() { 19 | y 20 | } else if x > y || (y.to_bits() == F::NEG_ZERO.to_bits() && x.is_sign_positive()) { 21 | x 22 | } else { 23 | y 24 | }; 25 | 26 | // Canonicalize 27 | res * F::ONE 28 | } 29 | -------------------------------------------------------------------------------- /libm/src/math/generic/fmaximum_num.rs: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT OR Apache-2.0 */ 2 | //! IEEE 754-2019 `maximumNumber`. 3 | //! 4 | //! Per the spec, returns: 5 | //! - `x` if `x > y` 6 | //! - `y` if `y > x` 7 | //! - Non-NaN if one operand is NaN 8 | //! - Logic following +0.0 > -0.0 9 | //! - Either `x` or `y` if `x == y` and the signs are the same 10 | //! - qNaN if either operand is a NaN 11 | //! 12 | //! Excluded from our implementation is sNaN handling. 13 | 14 | use super::super::Float; 15 | 16 | #[inline] 17 | pub fn fmaximum_num(x: F, y: F) -> F { 18 | let res = 19 | if x.is_nan() || x < y || (x.to_bits() == F::NEG_ZERO.to_bits() && y.is_sign_positive()) { 20 | y 21 | } else { 22 | x 23 | }; 24 | 25 | // Canonicalize 26 | res * F::ONE 27 | } 28 | -------------------------------------------------------------------------------- /libm/src/math/generic/fmin.rs: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT OR Apache-2.0 */ 2 | //! IEEE 754-2008 `minNum`. This has been superseded by IEEE 754-2019 `minimumNumber`. 3 | //! 4 | //! Per the spec, returns the canonicalized result of: 5 | //! - `x` if `x < y` 6 | //! - `y` if `y < x` 7 | //! - The other number if one is NaN 8 | //! - Otherwise, either `x` or `y`, canonicalized 9 | //! - -0.0 and +0.0 may be disregarded (unlike newer operations) 10 | //! 11 | //! Excluded from our implementation is sNaN handling. 12 | //! 13 | //! More on the differences: [link]. 14 | //! 15 | //! [link]: https://grouper.ieee.org/groups/msc/ANSI_IEEE-Std-754-2019/background/minNum_maxNum_Removal_Demotion_v3.pdf 16 | 17 | use super::super::Float; 18 | 19 | #[inline] 20 | pub fn fmin(x: F, y: F) -> F { 21 | let res = if y.is_nan() || x < y { x } else { y }; 22 | // Canonicalize 23 | res * F::ONE 24 | } 25 | -------------------------------------------------------------------------------- /libm/src/math/generic/fminimum.rs: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT OR Apache-2.0 */ 2 | //! IEEE 754-2019 `minimum`. 3 | //! 4 | //! Per the spec, returns the canonicalized result of: 5 | //! - `x` if `x < y` 6 | //! - `y` if `y < x` 7 | //! - qNaN if either operation is NaN 8 | //! - Logic following +0.0 > -0.0 9 | //! 10 | //! Excluded from our implementation is sNaN handling. 11 | 12 | use super::super::Float; 13 | 14 | #[inline] 15 | pub fn fminimum(x: F, y: F) -> F { 16 | let res = if x.is_nan() { 17 | x 18 | } else if y.is_nan() { 19 | y 20 | } else if x < y || (x.to_bits() == F::NEG_ZERO.to_bits() && y.is_sign_positive()) { 21 | x 22 | } else { 23 | y 24 | }; 25 | 26 | // Canonicalize 27 | res * F::ONE 28 | } 29 | -------------------------------------------------------------------------------- /libm/src/math/generic/fminimum_num.rs: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT OR Apache-2.0 */ 2 | //! IEEE 754-2019 `minimum`. 3 | //! 4 | //! Per the spec, returns: 5 | //! - `x` if `x < y` 6 | //! - `y` if `y < x` 7 | //! - Non-NaN if one operand is NaN 8 | //! - Logic following +0.0 > -0.0 9 | //! - Either `x` or `y` if `x == y` and the signs are the same 10 | //! - qNaN if either operand is a NaN 11 | //! 12 | //! Excluded from our implementation is sNaN handling. 13 | 14 | use super::super::Float; 15 | 16 | #[inline] 17 | pub fn fminimum_num(x: F, y: F) -> F { 18 | let res = 19 | if y.is_nan() || x < y || (x.to_bits() == F::NEG_ZERO.to_bits() && y.is_sign_positive()) { 20 | x 21 | } else { 22 | y 23 | }; 24 | 25 | // Canonicalize 26 | res * F::ONE 27 | } 28 | -------------------------------------------------------------------------------- /libm/src/math/generic/fmod.rs: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* origin: musl src/math/fmod.c. Ported to generic Rust algorithm in 2025, TG. */ 3 | 4 | use super::super::{CastFrom, Float, Int, MinInt}; 5 | 6 | #[inline] 7 | pub fn fmod(x: F, y: F) -> F { 8 | let zero = F::Int::ZERO; 9 | let one = F::Int::ONE; 10 | let mut ix = x.to_bits(); 11 | let mut iy = y.to_bits(); 12 | let mut ex = x.ex().signed(); 13 | let mut ey = y.ex().signed(); 14 | let sx = ix & F::SIGN_MASK; 15 | 16 | if iy << 1 == zero || y.is_nan() || ex == F::EXP_SAT as i32 { 17 | return (x * y) / (x * y); 18 | } 19 | 20 | if ix << 1 <= iy << 1 { 21 | if ix << 1 == iy << 1 { 22 | return F::ZERO * x; 23 | } 24 | return x; 25 | } 26 | 27 | /* normalize x and y */ 28 | if ex == 0 { 29 | let i = ix << (F::EXP_BITS + 1); 30 | ex -= i.leading_zeros() as i32; 31 | ix <<= -ex + 1; 32 | } else { 33 | ix &= F::Int::MAX >> F::EXP_BITS; 34 | ix |= one << F::SIG_BITS; 35 | } 36 | 37 | if ey == 0 { 38 | let i = iy << (F::EXP_BITS + 1); 39 | ey -= i.leading_zeros() as i32; 40 | iy <<= -ey + 1; 41 | } else { 42 | iy &= F::Int::MAX >> F::EXP_BITS; 43 | iy |= one << F::SIG_BITS; 44 | } 45 | 46 | /* x mod y */ 47 | while ex > ey { 48 | let i = ix.wrapping_sub(iy); 49 | if i >> (F::BITS - 1) == zero { 50 | if i == zero { 51 | return F::ZERO * x; 52 | } 53 | ix = i; 54 | } 55 | 56 | ix <<= 1; 57 | ex -= 1; 58 | } 59 | 60 | let i = ix.wrapping_sub(iy); 61 | if i >> (F::BITS - 1) == zero { 62 | if i == zero { 63 | return F::ZERO * x; 64 | } 65 | 66 | ix = i; 67 | } 68 | 69 | let shift = ix.leading_zeros().saturating_sub(F::EXP_BITS); 70 | ix <<= shift; 71 | ex -= shift as i32; 72 | 73 | /* scale result */ 74 | if ex > 0 { 75 | ix -= one << F::SIG_BITS; 76 | ix |= F::Int::cast_from(ex) << F::SIG_BITS; 77 | } else { 78 | ix >>= -ex + 1; 79 | } 80 | 81 | ix |= sx; 82 | 83 | F::from_bits(ix) 84 | } 85 | -------------------------------------------------------------------------------- /libm/src/math/generic/mod.rs: -------------------------------------------------------------------------------- 1 | // Note: generic functions are marked `#[inline]` because, even though generic functions are 2 | // typically inlined, this does not seem to always be the case. 3 | 4 | mod ceil; 5 | mod copysign; 6 | mod fabs; 7 | mod fdim; 8 | mod floor; 9 | mod fmax; 10 | mod fmaximum; 11 | mod fmaximum_num; 12 | mod fmin; 13 | mod fminimum; 14 | mod fminimum_num; 15 | mod fmod; 16 | mod rint; 17 | mod round; 18 | mod scalbn; 19 | mod sqrt; 20 | mod trunc; 21 | 22 | pub use ceil::ceil; 23 | pub use copysign::copysign; 24 | pub use fabs::fabs; 25 | pub use fdim::fdim; 26 | pub use floor::floor; 27 | pub use fmax::fmax; 28 | pub use fmaximum::fmaximum; 29 | pub use fmaximum_num::fmaximum_num; 30 | pub use fmin::fmin; 31 | pub use fminimum::fminimum; 32 | pub use fminimum_num::fminimum_num; 33 | pub use fmod::fmod; 34 | pub use rint::rint_round; 35 | pub use round::round; 36 | pub use scalbn::scalbn; 37 | pub use sqrt::sqrt; 38 | pub use trunc::trunc; 39 | -------------------------------------------------------------------------------- /libm/src/math/generic/round.rs: -------------------------------------------------------------------------------- 1 | use super::super::{Float, MinInt}; 2 | use super::{copysign, trunc}; 3 | 4 | #[inline] 5 | pub fn round(x: F) -> F { 6 | let f0p5 = F::from_parts(false, F::EXP_BIAS - 1, F::Int::ZERO); // 0.5 7 | let f0p25 = F::from_parts(false, F::EXP_BIAS - 2, F::Int::ZERO); // 0.25 8 | 9 | trunc(x + copysign(f0p5 - f0p25 * F::EPSILON, x)) 10 | } 11 | 12 | #[cfg(test)] 13 | mod tests { 14 | use super::*; 15 | 16 | #[test] 17 | #[cfg(f16_enabled)] 18 | fn zeroes_f16() { 19 | assert_biteq!(round(0.0_f16), 0.0_f16); 20 | assert_biteq!(round(-0.0_f16), -0.0_f16); 21 | } 22 | 23 | #[test] 24 | #[cfg(f16_enabled)] 25 | fn sanity_check_f16() { 26 | assert_eq!(round(-1.0_f16), -1.0); 27 | assert_eq!(round(2.8_f16), 3.0); 28 | assert_eq!(round(-0.5_f16), -1.0); 29 | assert_eq!(round(0.5_f16), 1.0); 30 | assert_eq!(round(-1.5_f16), -2.0); 31 | assert_eq!(round(1.5_f16), 2.0); 32 | } 33 | 34 | #[test] 35 | fn zeroes_f32() { 36 | assert_biteq!(round(0.0_f32), 0.0_f32); 37 | assert_biteq!(round(-0.0_f32), -0.0_f32); 38 | } 39 | 40 | #[test] 41 | fn sanity_check_f32() { 42 | assert_eq!(round(-1.0_f32), -1.0); 43 | assert_eq!(round(2.8_f32), 3.0); 44 | assert_eq!(round(-0.5_f32), -1.0); 45 | assert_eq!(round(0.5_f32), 1.0); 46 | assert_eq!(round(-1.5_f32), -2.0); 47 | assert_eq!(round(1.5_f32), 2.0); 48 | } 49 | 50 | #[test] 51 | fn zeroes_f64() { 52 | assert_biteq!(round(0.0_f64), 0.0_f64); 53 | assert_biteq!(round(-0.0_f64), -0.0_f64); 54 | } 55 | 56 | #[test] 57 | fn sanity_check_f64() { 58 | assert_eq!(round(-1.0_f64), -1.0); 59 | assert_eq!(round(2.8_f64), 3.0); 60 | assert_eq!(round(-0.5_f64), -1.0); 61 | assert_eq!(round(0.5_f64), 1.0); 62 | assert_eq!(round(-1.5_f64), -2.0); 63 | assert_eq!(round(1.5_f64), 2.0); 64 | } 65 | 66 | #[test] 67 | #[cfg(f128_enabled)] 68 | fn zeroes_f128() { 69 | assert_biteq!(round(0.0_f128), 0.0_f128); 70 | assert_biteq!(round(-0.0_f128), -0.0_f128); 71 | } 72 | 73 | #[test] 74 | #[cfg(f128_enabled)] 75 | fn sanity_check_f128() { 76 | assert_eq!(round(-1.0_f128), -1.0); 77 | assert_eq!(round(2.8_f128), 3.0); 78 | assert_eq!(round(-0.5_f128), -1.0); 79 | assert_eq!(round(0.5_f128), 1.0); 80 | assert_eq!(round(-1.5_f128), -2.0); 81 | assert_eq!(round(1.5_f128), 2.0); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /libm/src/math/hypot.rs: -------------------------------------------------------------------------------- 1 | use core::f64; 2 | 3 | use super::sqrt; 4 | 5 | const SPLIT: f64 = 134217728. + 1.; // 0x1p27 + 1 === (2 ^ 27) + 1 6 | 7 | fn sq(x: f64) -> (f64, f64) { 8 | let xh: f64; 9 | let xl: f64; 10 | let xc: f64; 11 | 12 | xc = x * SPLIT; 13 | xh = x - xc + xc; 14 | xl = x - xh; 15 | let hi = x * x; 16 | let lo = xh * xh - hi + 2. * xh * xl + xl * xl; 17 | (hi, lo) 18 | } 19 | 20 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 21 | pub fn hypot(mut x: f64, mut y: f64) -> f64 { 22 | let x1p700 = f64::from_bits(0x6bb0000000000000); // 0x1p700 === 2 ^ 700 23 | let x1p_700 = f64::from_bits(0x1430000000000000); // 0x1p-700 === 2 ^ -700 24 | 25 | let mut uxi = x.to_bits(); 26 | let mut uyi = y.to_bits(); 27 | let uti; 28 | let ex: i64; 29 | let ey: i64; 30 | let mut z: f64; 31 | 32 | /* arrange |x| >= |y| */ 33 | uxi &= -1i64 as u64 >> 1; 34 | uyi &= -1i64 as u64 >> 1; 35 | if uxi < uyi { 36 | uti = uxi; 37 | uxi = uyi; 38 | uyi = uti; 39 | } 40 | 41 | /* special cases */ 42 | ex = (uxi >> 52) as i64; 43 | ey = (uyi >> 52) as i64; 44 | x = f64::from_bits(uxi); 45 | y = f64::from_bits(uyi); 46 | /* note: hypot(inf,nan) == inf */ 47 | if ey == 0x7ff { 48 | return y; 49 | } 50 | if ex == 0x7ff || uyi == 0 { 51 | return x; 52 | } 53 | /* note: hypot(x,y) ~= x + y*y/x/2 with inexact for small y/x */ 54 | /* 64 difference is enough for ld80 double_t */ 55 | if ex - ey > 64 { 56 | return x + y; 57 | } 58 | 59 | /* precise sqrt argument in nearest rounding mode without overflow */ 60 | /* xh*xh must not overflow and xl*xl must not underflow in sq */ 61 | z = 1.; 62 | if ex > 0x3ff + 510 { 63 | z = x1p700; 64 | x *= x1p_700; 65 | y *= x1p_700; 66 | } else if ey < 0x3ff - 450 { 67 | z = x1p_700; 68 | x *= x1p700; 69 | y *= x1p700; 70 | } 71 | let (hx, lx) = sq(x); 72 | let (hy, ly) = sq(y); 73 | z * sqrt(ly + lx + hy + hx) 74 | } 75 | -------------------------------------------------------------------------------- /libm/src/math/hypotf.rs: -------------------------------------------------------------------------------- 1 | use core::f32; 2 | 3 | use super::sqrtf; 4 | 5 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 6 | pub fn hypotf(mut x: f32, mut y: f32) -> f32 { 7 | let x1p90 = f32::from_bits(0x6c800000); // 0x1p90f === 2 ^ 90 8 | let x1p_90 = f32::from_bits(0x12800000); // 0x1p-90f === 2 ^ -90 9 | 10 | let mut uxi = x.to_bits(); 11 | let mut uyi = y.to_bits(); 12 | let uti; 13 | let mut z: f32; 14 | 15 | uxi &= -1i32 as u32 >> 1; 16 | uyi &= -1i32 as u32 >> 1; 17 | if uxi < uyi { 18 | uti = uxi; 19 | uxi = uyi; 20 | uyi = uti; 21 | } 22 | 23 | x = f32::from_bits(uxi); 24 | y = f32::from_bits(uyi); 25 | if uyi == 0xff << 23 { 26 | return y; 27 | } 28 | if uxi >= 0xff << 23 || uyi == 0 || uxi - uyi >= 25 << 23 { 29 | return x + y; 30 | } 31 | 32 | z = 1.; 33 | if uxi >= (0x7f + 60) << 23 { 34 | z = x1p90; 35 | x *= x1p_90; 36 | y *= x1p_90; 37 | } else if uyi < (0x7f - 60) << 23 { 38 | z = x1p_90; 39 | x *= x1p90; 40 | y *= x1p90; 41 | } 42 | z * sqrtf((x as f64 * x as f64 + y as f64 * y as f64) as f32) 43 | } 44 | -------------------------------------------------------------------------------- /libm/src/math/ilogb.rs: -------------------------------------------------------------------------------- 1 | const FP_ILOGBNAN: i32 = -1 - 0x7fffffff; 2 | const FP_ILOGB0: i32 = FP_ILOGBNAN; 3 | 4 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 5 | pub fn ilogb(x: f64) -> i32 { 6 | let mut i: u64 = x.to_bits(); 7 | let e = ((i >> 52) & 0x7ff) as i32; 8 | 9 | if e == 0 { 10 | i <<= 12; 11 | if i == 0 { 12 | force_eval!(0.0 / 0.0); 13 | return FP_ILOGB0; 14 | } 15 | /* subnormal x */ 16 | let mut e = -0x3ff; 17 | while (i >> 63) == 0 { 18 | e -= 1; 19 | i <<= 1; 20 | } 21 | e 22 | } else if e == 0x7ff { 23 | force_eval!(0.0 / 0.0); 24 | if (i << 12) != 0 { FP_ILOGBNAN } else { i32::MAX } 25 | } else { 26 | e - 0x3ff 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /libm/src/math/ilogbf.rs: -------------------------------------------------------------------------------- 1 | const FP_ILOGBNAN: i32 = -1 - 0x7fffffff; 2 | const FP_ILOGB0: i32 = FP_ILOGBNAN; 3 | 4 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 5 | pub fn ilogbf(x: f32) -> i32 { 6 | let mut i = x.to_bits(); 7 | let e = ((i >> 23) & 0xff) as i32; 8 | 9 | if e == 0 { 10 | i <<= 9; 11 | if i == 0 { 12 | force_eval!(0.0 / 0.0); 13 | return FP_ILOGB0; 14 | } 15 | /* subnormal x */ 16 | let mut e = -0x7f; 17 | while (i >> 31) == 0 { 18 | e -= 1; 19 | i <<= 1; 20 | } 21 | e 22 | } else if e == 0xff { 23 | force_eval!(0.0 / 0.0); 24 | if (i << 9) != 0 { FP_ILOGBNAN } else { i32::MAX } 25 | } else { 26 | e - 0x7f 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /libm/src/math/k_cos.rs: -------------------------------------------------------------------------------- 1 | // origin: FreeBSD /usr/src/lib/msun/src/k_cos.c 2 | // 3 | // ==================================================== 4 | // Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 5 | // 6 | // Developed at SunSoft, a Sun Microsystems, Inc. business. 7 | // Permission to use, copy, modify, and distribute this 8 | // software is freely granted, provided that this notice 9 | // is preserved. 10 | // ==================================================== 11 | 12 | const C1: f64 = 4.16666666666666019037e-02; /* 0x3FA55555, 0x5555554C */ 13 | const C2: f64 = -1.38888888888741095749e-03; /* 0xBF56C16C, 0x16C15177 */ 14 | const C3: f64 = 2.48015872894767294178e-05; /* 0x3EFA01A0, 0x19CB1590 */ 15 | const C4: f64 = -2.75573143513906633035e-07; /* 0xBE927E4F, 0x809C52AD */ 16 | const C5: f64 = 2.08757232129817482790e-09; /* 0x3E21EE9E, 0xBDB4B1C4 */ 17 | const C6: f64 = -1.13596475577881948265e-11; /* 0xBDA8FAE9, 0xBE8838D4 */ 18 | 19 | // kernel cos function on [-pi/4, pi/4], pi/4 ~ 0.785398164 20 | // Input x is assumed to be bounded by ~pi/4 in magnitude. 21 | // Input y is the tail of x. 22 | // 23 | // Algorithm 24 | // 1. Since cos(-x) = cos(x), we need only to consider positive x. 25 | // 2. if x < 2^-27 (hx<0x3e400000 0), return 1 with inexact if x!=0. 26 | // 3. cos(x) is approximated by a polynomial of degree 14 on 27 | // [0,pi/4] 28 | // 4 14 29 | // cos(x) ~ 1 - x*x/2 + C1*x + ... + C6*x 30 | // where the remez error is 31 | // 32 | // | 2 4 6 8 10 12 14 | -58 33 | // |cos(x)-(1-.5*x +C1*x +C2*x +C3*x +C4*x +C5*x +C6*x )| <= 2 34 | // | | 35 | // 36 | // 4 6 8 10 12 14 37 | // 4. let r = C1*x +C2*x +C3*x +C4*x +C5*x +C6*x , then 38 | // cos(x) ~ 1 - x*x/2 + r 39 | // since cos(x+y) ~ cos(x) - sin(x)*y 40 | // ~ cos(x) - x*y, 41 | // a correction term is necessary in cos(x) and hence 42 | // cos(x+y) = 1 - (x*x/2 - (r - x*y)) 43 | // For better accuracy, rearrange to 44 | // cos(x+y) ~ w + (tmp + (r-x*y)) 45 | // where w = 1 - x*x/2 and tmp is a tiny correction term 46 | // (1 - x*x/2 == w + tmp exactly in infinite precision). 47 | // The exactness of w + tmp in infinite precision depends on w 48 | // and tmp having the same precision as x. If they have extra 49 | // precision due to compiler bugs, then the extra precision is 50 | // only good provided it is retained in all terms of the final 51 | // expression for cos(). Retention happens in all cases tested 52 | // under FreeBSD, so don't pessimize things by forcibly clipping 53 | // any extra precision in w. 54 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 55 | pub(crate) fn k_cos(x: f64, y: f64) -> f64 { 56 | let z = x * x; 57 | let w = z * z; 58 | let r = z * (C1 + z * (C2 + z * C3)) + w * w * (C4 + z * (C5 + z * C6)); 59 | let hz = 0.5 * z; 60 | let w = 1.0 - hz; 61 | w + (((1.0 - w) - hz) + (z * r - x * y)) 62 | } 63 | -------------------------------------------------------------------------------- /libm/src/math/k_cosf.rs: -------------------------------------------------------------------------------- 1 | /* origin: FreeBSD /usr/src/lib/msun/src/k_cosf.c */ 2 | /* 3 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 4 | * Debugged and optimized by Bruce D. Evans. 5 | */ 6 | /* 7 | * ==================================================== 8 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 9 | * 10 | * Developed at SunPro, a Sun Microsystems, Inc. business. 11 | * Permission to use, copy, modify, and distribute this 12 | * software is freely granted, provided that this notice 13 | * is preserved. 14 | * ==================================================== 15 | */ 16 | 17 | /* |cos(x) - c(x)| < 2**-34.1 (~[-5.37e-11, 5.295e-11]). */ 18 | const C0: f64 = -0.499999997251031003120; /* -0x1ffffffd0c5e81.0p-54 */ 19 | const C1: f64 = 0.0416666233237390631894; /* 0x155553e1053a42.0p-57 */ 20 | const C2: f64 = -0.00138867637746099294692; /* -0x16c087e80f1e27.0p-62 */ 21 | const C3: f64 = 0.0000243904487962774090654; /* 0x199342e0ee5069.0p-68 */ 22 | 23 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 24 | pub(crate) fn k_cosf(x: f64) -> f32 { 25 | let z = x * x; 26 | let w = z * z; 27 | let r = C2 + z * C3; 28 | (((1.0 + z * C0) + w * C1) + (w * z) * r) as f32 29 | } 30 | -------------------------------------------------------------------------------- /libm/src/math/k_expo2.rs: -------------------------------------------------------------------------------- 1 | use super::exp; 2 | 3 | /* k is such that k*ln2 has minimal relative error and x - kln2 > log(FLT_MIN) */ 4 | const K: i32 = 2043; 5 | 6 | /* expf(x)/2 for x >= log(FLT_MAX), slightly better than 0.5f*expf(x/2)*expf(x/2) */ 7 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 8 | pub(crate) fn k_expo2(x: f64) -> f64 { 9 | let k_ln2 = f64::from_bits(0x40962066151add8b); 10 | /* note that k is odd and scale*scale overflows */ 11 | let scale = f64::from_bits(((((0x3ff + K / 2) as u32) << 20) as u64) << 32); 12 | /* exp(x - k ln2) * 2**(k-1) */ 13 | exp(x - k_ln2) * scale * scale 14 | } 15 | -------------------------------------------------------------------------------- /libm/src/math/k_expo2f.rs: -------------------------------------------------------------------------------- 1 | use super::expf; 2 | 3 | /* k is such that k*ln2 has minimal relative error and x - kln2 > log(FLT_MIN) */ 4 | const K: i32 = 235; 5 | 6 | /* expf(x)/2 for x >= log(FLT_MAX), slightly better than 0.5f*expf(x/2)*expf(x/2) */ 7 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 8 | pub(crate) fn k_expo2f(x: f32) -> f32 { 9 | let k_ln2 = f32::from_bits(0x4322e3bc); 10 | /* note that k is odd and scale*scale overflows */ 11 | let scale = f32::from_bits(((0x7f + K / 2) as u32) << 23); 12 | /* exp(x - k ln2) * 2**(k-1) */ 13 | expf(x - k_ln2) * scale * scale 14 | } 15 | -------------------------------------------------------------------------------- /libm/src/math/k_sin.rs: -------------------------------------------------------------------------------- 1 | // origin: FreeBSD /usr/src/lib/msun/src/k_sin.c 2 | // 3 | // ==================================================== 4 | // Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 5 | // 6 | // Developed at SunSoft, a Sun Microsystems, Inc. business. 7 | // Permission to use, copy, modify, and distribute this 8 | // software is freely granted, provided that this notice 9 | // is preserved. 10 | // ==================================================== 11 | 12 | const S1: f64 = -1.66666666666666324348e-01; /* 0xBFC55555, 0x55555549 */ 13 | const S2: f64 = 8.33333333332248946124e-03; /* 0x3F811111, 0x1110F8A6 */ 14 | const S3: f64 = -1.98412698298579493134e-04; /* 0xBF2A01A0, 0x19C161D5 */ 15 | const S4: f64 = 2.75573137070700676789e-06; /* 0x3EC71DE3, 0x57B1FE7D */ 16 | const S5: f64 = -2.50507602534068634195e-08; /* 0xBE5AE5E6, 0x8A2B9CEB */ 17 | const S6: f64 = 1.58969099521155010221e-10; /* 0x3DE5D93A, 0x5ACFD57C */ 18 | 19 | // kernel sin function on ~[-pi/4, pi/4] (except on -0), pi/4 ~ 0.7854 20 | // Input x is assumed to be bounded by ~pi/4 in magnitude. 21 | // Input y is the tail of x. 22 | // Input iy indicates whether y is 0. (if iy=0, y assume to be 0). 23 | // 24 | // Algorithm 25 | // 1. Since sin(-x) = -sin(x), we need only to consider positive x. 26 | // 2. Callers must return sin(-0) = -0 without calling here since our 27 | // odd polynomial is not evaluated in a way that preserves -0. 28 | // Callers may do the optimization sin(x) ~ x for tiny x. 29 | // 3. sin(x) is approximated by a polynomial of degree 13 on 30 | // [0,pi/4] 31 | // 3 13 32 | // sin(x) ~ x + S1*x + ... + S6*x 33 | // where 34 | // 35 | // |sin(x) 2 4 6 8 10 12 | -58 36 | // |----- - (1+S1*x +S2*x +S3*x +S4*x +S5*x +S6*x )| <= 2 37 | // | x | 38 | // 39 | // 4. sin(x+y) = sin(x) + sin'(x')*y 40 | // ~ sin(x) + (1-x*x/2)*y 41 | // For better accuracy, let 42 | // 3 2 2 2 2 43 | // r = x *(S2+x *(S3+x *(S4+x *(S5+x *S6)))) 44 | // then 3 2 45 | // sin(x) = x + (S1*x + (x *(r-y/2)+y)) 46 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 47 | pub(crate) fn k_sin(x: f64, y: f64, iy: i32) -> f64 { 48 | let z = x * x; 49 | let w = z * z; 50 | let r = S2 + z * (S3 + z * S4) + z * w * (S5 + z * S6); 51 | let v = z * x; 52 | if iy == 0 { x + v * (S1 + z * r) } else { x - ((z * (0.5 * y - v * r) - y) - v * S1) } 53 | } 54 | -------------------------------------------------------------------------------- /libm/src/math/k_sinf.rs: -------------------------------------------------------------------------------- 1 | /* origin: FreeBSD /usr/src/lib/msun/src/k_sinf.c */ 2 | /* 3 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 4 | * Optimized by Bruce D. Evans. 5 | */ 6 | /* 7 | * ==================================================== 8 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 9 | * 10 | * Developed at SunPro, a Sun Microsystems, Inc. business. 11 | * Permission to use, copy, modify, and distribute this 12 | * software is freely granted, provided that this notice 13 | * is preserved. 14 | * ==================================================== 15 | */ 16 | 17 | /* |sin(x)/x - s(x)| < 2**-37.5 (~[-4.89e-12, 4.824e-12]). */ 18 | const S1: f64 = -0.166666666416265235595; /* -0x15555554cbac77.0p-55 */ 19 | const S2: f64 = 0.0083333293858894631756; /* 0x111110896efbb2.0p-59 */ 20 | const S3: f64 = -0.000198393348360966317347; /* -0x1a00f9e2cae774.0p-65 */ 21 | const S4: f64 = 0.0000027183114939898219064; /* 0x16cd878c3b46a7.0p-71 */ 22 | 23 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 24 | pub(crate) fn k_sinf(x: f64) -> f32 { 25 | let z = x * x; 26 | let w = z * z; 27 | let r = S3 + z * S4; 28 | let s = z * x; 29 | ((x + s * (S1 + z * S2)) + s * w * r) as f32 30 | } 31 | -------------------------------------------------------------------------------- /libm/src/math/k_tanf.rs: -------------------------------------------------------------------------------- 1 | /* origin: FreeBSD /usr/src/lib/msun/src/k_tan.c */ 2 | /* 3 | * ==================================================== 4 | * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. 5 | * 6 | * Permission to use, copy, modify, and distribute this 7 | * software is freely granted, provided that this notice 8 | * is preserved. 9 | * ==================================================== 10 | */ 11 | 12 | /* |tan(x)/x - t(x)| < 2**-25.5 (~[-2e-08, 2e-08]). */ 13 | const T: [f64; 6] = [ 14 | 0.333331395030791399758, /* 0x15554d3418c99f.0p-54 */ 15 | 0.133392002712976742718, /* 0x1112fd38999f72.0p-55 */ 16 | 0.0533812378445670393523, /* 0x1b54c91d865afe.0p-57 */ 17 | 0.0245283181166547278873, /* 0x191df3908c33ce.0p-58 */ 18 | 0.00297435743359967304927, /* 0x185dadfcecf44e.0p-61 */ 19 | 0.00946564784943673166728, /* 0x1362b9bf971bcd.0p-59 */ 20 | ]; 21 | 22 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 23 | pub(crate) fn k_tanf(x: f64, odd: bool) -> f32 { 24 | let z = x * x; 25 | /* 26 | * Split up the polynomial into small independent terms to give 27 | * opportunities for parallel evaluation. The chosen splitting is 28 | * micro-optimized for Athlons (XP, X64). It costs 2 multiplications 29 | * relative to Horner's method on sequential machines. 30 | * 31 | * We add the small terms from lowest degree up for efficiency on 32 | * non-sequential machines (the lowest degree terms tend to be ready 33 | * earlier). Apart from this, we don't care about order of 34 | * operations, and don't need to to care since we have precision to 35 | * spare. However, the chosen splitting is good for accuracy too, 36 | * and would give results as accurate as Horner's method if the 37 | * small terms were added from highest degree down. 38 | */ 39 | let mut r = T[4] + z * T[5]; 40 | let t = T[2] + z * T[3]; 41 | let w = z * z; 42 | let s = z * x; 43 | let u = T[0] + z * T[1]; 44 | r = (x + s * u) + (s * w) * (t + w * r); 45 | (if odd { -1. / r } else { r }) as f32 46 | } 47 | -------------------------------------------------------------------------------- /libm/src/math/ldexp.rs: -------------------------------------------------------------------------------- 1 | #[cfg(f16_enabled)] 2 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 3 | pub fn ldexpf16(x: f16, n: i32) -> f16 { 4 | super::scalbnf16(x, n) 5 | } 6 | 7 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 8 | pub fn ldexpf(x: f32, n: i32) -> f32 { 9 | super::scalbnf(x, n) 10 | } 11 | 12 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 13 | pub fn ldexp(x: f64, n: i32) -> f64 { 14 | super::scalbn(x, n) 15 | } 16 | 17 | #[cfg(f128_enabled)] 18 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 19 | pub fn ldexpf128(x: f128, n: i32) -> f128 { 20 | super::scalbnf128(x, n) 21 | } 22 | -------------------------------------------------------------------------------- /libm/src/math/ldexpf.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 2 | pub fn ldexpf(x: f32, n: i32) -> f32 { 3 | super::scalbnf(x, n) 4 | } 5 | -------------------------------------------------------------------------------- /libm/src/math/ldexpf128.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 2 | pub fn ldexpf128(x: f128, n: i32) -> f128 { 3 | super::scalbnf128(x, n) 4 | } 5 | -------------------------------------------------------------------------------- /libm/src/math/ldexpf16.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 2 | pub fn ldexpf16(x: f16, n: i32) -> f16 { 3 | super::scalbnf16(x, n) 4 | } 5 | -------------------------------------------------------------------------------- /libm/src/math/lgamma.rs: -------------------------------------------------------------------------------- 1 | use super::lgamma_r; 2 | 3 | /// The natural logarithm of the 4 | /// [Gamma function](https://en.wikipedia.org/wiki/Gamma_function) (f64). 5 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 6 | pub fn lgamma(x: f64) -> f64 { 7 | lgamma_r(x).0 8 | } 9 | -------------------------------------------------------------------------------- /libm/src/math/lgammaf.rs: -------------------------------------------------------------------------------- 1 | use super::lgammaf_r; 2 | 3 | /// The natural logarithm of the 4 | /// [Gamma function](https://en.wikipedia.org/wiki/Gamma_function) (f32). 5 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 6 | pub fn lgammaf(x: f32) -> f32 { 7 | lgammaf_r(x).0 8 | } 9 | -------------------------------------------------------------------------------- /libm/src/math/log10f.rs: -------------------------------------------------------------------------------- 1 | /* origin: FreeBSD /usr/src/lib/msun/src/e_log10f.c */ 2 | /* 3 | * ==================================================== 4 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 5 | * 6 | * Developed at SunPro, a Sun Microsystems, Inc. business. 7 | * Permission to use, copy, modify, and distribute this 8 | * software is freely granted, provided that this notice 9 | * is preserved. 10 | * ==================================================== 11 | */ 12 | /* 13 | * See comments in log10.c. 14 | */ 15 | 16 | use core::f32; 17 | 18 | const IVLN10HI: f32 = 4.3432617188e-01; /* 0x3ede6000 */ 19 | const IVLN10LO: f32 = -3.1689971365e-05; /* 0xb804ead9 */ 20 | const LOG10_2HI: f32 = 3.0102920532e-01; /* 0x3e9a2080 */ 21 | const LOG10_2LO: f32 = 7.9034151668e-07; /* 0x355427db */ 22 | /* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */ 23 | const LG1: f32 = 0.66666662693; /* 0xaaaaaa.0p-24 */ 24 | const LG2: f32 = 0.40000972152; /* 0xccce13.0p-25 */ 25 | const LG3: f32 = 0.28498786688; /* 0x91e9ee.0p-25 */ 26 | const LG4: f32 = 0.24279078841; /* 0xf89e26.0p-26 */ 27 | 28 | /// The base 10 logarithm of `x` (f32). 29 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 30 | pub fn log10f(mut x: f32) -> f32 { 31 | let x1p25f = f32::from_bits(0x4c000000); // 0x1p25f === 2 ^ 25 32 | 33 | let mut ui: u32 = x.to_bits(); 34 | let hfsq: f32; 35 | let f: f32; 36 | let s: f32; 37 | let z: f32; 38 | let r: f32; 39 | let w: f32; 40 | let t1: f32; 41 | let t2: f32; 42 | let dk: f32; 43 | let mut hi: f32; 44 | let lo: f32; 45 | let mut ix: u32; 46 | let mut k: i32; 47 | 48 | ix = ui; 49 | k = 0; 50 | if ix < 0x00800000 || (ix >> 31) > 0 { 51 | /* x < 2**-126 */ 52 | if ix << 1 == 0 { 53 | return -1. / (x * x); /* log(+-0)=-inf */ 54 | } 55 | if (ix >> 31) > 0 { 56 | return (x - x) / 0.0; /* log(-#) = NaN */ 57 | } 58 | /* subnormal number, scale up x */ 59 | k -= 25; 60 | x *= x1p25f; 61 | ui = x.to_bits(); 62 | ix = ui; 63 | } else if ix >= 0x7f800000 { 64 | return x; 65 | } else if ix == 0x3f800000 { 66 | return 0.; 67 | } 68 | 69 | /* reduce x into [sqrt(2)/2, sqrt(2)] */ 70 | ix += 0x3f800000 - 0x3f3504f3; 71 | k += (ix >> 23) as i32 - 0x7f; 72 | ix = (ix & 0x007fffff) + 0x3f3504f3; 73 | ui = ix; 74 | x = f32::from_bits(ui); 75 | 76 | f = x - 1.0; 77 | s = f / (2.0 + f); 78 | z = s * s; 79 | w = z * z; 80 | t1 = w * (LG2 + w * LG4); 81 | t2 = z * (LG1 + w * LG3); 82 | r = t2 + t1; 83 | hfsq = 0.5 * f * f; 84 | 85 | hi = f - hfsq; 86 | ui = hi.to_bits(); 87 | ui &= 0xfffff000; 88 | hi = f32::from_bits(ui); 89 | lo = f - hi - hfsq + s * (hfsq + r); 90 | dk = k as f32; 91 | dk * LOG10_2LO + (lo + hi) * IVLN10LO + lo * IVLN10HI + hi * IVLN10HI + dk * LOG10_2HI 92 | } 93 | -------------------------------------------------------------------------------- /libm/src/math/log1pf.rs: -------------------------------------------------------------------------------- 1 | /* origin: FreeBSD /usr/src/lib/msun/src/s_log1pf.c */ 2 | /* 3 | * ==================================================== 4 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 5 | * 6 | * Developed at SunPro, a Sun Microsystems, Inc. business. 7 | * Permission to use, copy, modify, and distribute this 8 | * software is freely granted, provided that this notice 9 | * is preserved. 10 | * ==================================================== 11 | */ 12 | 13 | use core::f32; 14 | 15 | const LN2_HI: f32 = 6.9313812256e-01; /* 0x3f317180 */ 16 | const LN2_LO: f32 = 9.0580006145e-06; /* 0x3717f7d1 */ 17 | /* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */ 18 | const LG1: f32 = 0.66666662693; /* 0xaaaaaa.0p-24 */ 19 | const LG2: f32 = 0.40000972152; /* 0xccce13.0p-25 */ 20 | const LG3: f32 = 0.28498786688; /* 0x91e9ee.0p-25 */ 21 | const LG4: f32 = 0.24279078841; /* 0xf89e26.0p-26 */ 22 | 23 | /// The natural logarithm of 1+`x` (f32). 24 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 25 | pub fn log1pf(x: f32) -> f32 { 26 | let mut ui: u32 = x.to_bits(); 27 | let hfsq: f32; 28 | let mut f: f32 = 0.; 29 | let mut c: f32 = 0.; 30 | let s: f32; 31 | let z: f32; 32 | let r: f32; 33 | let w: f32; 34 | let t1: f32; 35 | let t2: f32; 36 | let dk: f32; 37 | let ix: u32; 38 | let mut iu: u32; 39 | let mut k: i32; 40 | 41 | ix = ui; 42 | k = 1; 43 | if ix < 0x3ed413d0 || (ix >> 31) > 0 { 44 | /* 1+x < sqrt(2)+ */ 45 | if ix >= 0xbf800000 { 46 | /* x <= -1.0 */ 47 | if x == -1. { 48 | return x / 0.0; /* log1p(-1)=+inf */ 49 | } 50 | return (x - x) / 0.0; /* log1p(x<-1)=NaN */ 51 | } 52 | if ix << 1 < 0x33800000 << 1 { 53 | /* |x| < 2**-24 */ 54 | /* underflow if subnormal */ 55 | if (ix & 0x7f800000) == 0 { 56 | force_eval!(x * x); 57 | } 58 | return x; 59 | } 60 | if ix <= 0xbe95f619 { 61 | /* sqrt(2)/2- <= 1+x < sqrt(2)+ */ 62 | k = 0; 63 | c = 0.; 64 | f = x; 65 | } 66 | } else if ix >= 0x7f800000 { 67 | return x; 68 | } 69 | if k > 0 { 70 | ui = (1. + x).to_bits(); 71 | iu = ui; 72 | iu += 0x3f800000 - 0x3f3504f3; 73 | k = (iu >> 23) as i32 - 0x7f; 74 | /* correction term ~ log(1+x)-log(u), avoid underflow in c/u */ 75 | if k < 25 { 76 | c = if k >= 2 { 1. - (f32::from_bits(ui) - x) } else { x - (f32::from_bits(ui) - 1.) }; 77 | c /= f32::from_bits(ui); 78 | } else { 79 | c = 0.; 80 | } 81 | /* reduce u into [sqrt(2)/2, sqrt(2)] */ 82 | iu = (iu & 0x007fffff) + 0x3f3504f3; 83 | ui = iu; 84 | f = f32::from_bits(ui) - 1.; 85 | } 86 | s = f / (2.0 + f); 87 | z = s * s; 88 | w = z * z; 89 | t1 = w * (LG2 + w * LG4); 90 | t2 = z * (LG1 + w * LG3); 91 | r = t2 + t1; 92 | hfsq = 0.5 * f * f; 93 | dk = k as f32; 94 | s * (hfsq + r) + (dk * LN2_LO + c) - hfsq + f + dk * LN2_HI 95 | } 96 | -------------------------------------------------------------------------------- /libm/src/math/log2.rs: -------------------------------------------------------------------------------- 1 | /* origin: FreeBSD /usr/src/lib/msun/src/e_log2.c */ 2 | /* 3 | * ==================================================== 4 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 5 | * 6 | * Developed at SunSoft, a Sun Microsystems, Inc. business. 7 | * Permission to use, copy, modify, and distribute this 8 | * software is freely granted, provided that this notice 9 | * is preserved. 10 | * ==================================================== 11 | */ 12 | /* 13 | * Return the base 2 logarithm of x. See log.c for most comments. 14 | * 15 | * Reduce x to 2^k (1+f) and calculate r = log(1+f) - f + f*f/2 16 | * as in log.c, then combine and scale in extra precision: 17 | * log2(x) = (f - f*f/2 + r)/log(2) + k 18 | */ 19 | 20 | use core::f64; 21 | 22 | const IVLN2HI: f64 = 1.44269504072144627571e+00; /* 0x3ff71547, 0x65200000 */ 23 | const IVLN2LO: f64 = 1.67517131648865118353e-10; /* 0x3de705fc, 0x2eefa200 */ 24 | const LG1: f64 = 6.666666666666735130e-01; /* 3FE55555 55555593 */ 25 | const LG2: f64 = 3.999999999940941908e-01; /* 3FD99999 9997FA04 */ 26 | const LG3: f64 = 2.857142874366239149e-01; /* 3FD24924 94229359 */ 27 | const LG4: f64 = 2.222219843214978396e-01; /* 3FCC71C5 1D8E78AF */ 28 | const LG5: f64 = 1.818357216161805012e-01; /* 3FC74664 96CB03DE */ 29 | const LG6: f64 = 1.531383769920937332e-01; /* 3FC39A09 D078C69F */ 30 | const LG7: f64 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ 31 | 32 | /// The base 2 logarithm of `x` (f64). 33 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 34 | pub fn log2(mut x: f64) -> f64 { 35 | let x1p54 = f64::from_bits(0x4350000000000000); // 0x1p54 === 2 ^ 54 36 | 37 | let mut ui: u64 = x.to_bits(); 38 | let hfsq: f64; 39 | let f: f64; 40 | let s: f64; 41 | let z: f64; 42 | let r: f64; 43 | let mut w: f64; 44 | let t1: f64; 45 | let t2: f64; 46 | let y: f64; 47 | let mut hi: f64; 48 | let lo: f64; 49 | let mut val_hi: f64; 50 | let mut val_lo: f64; 51 | let mut hx: u32; 52 | let mut k: i32; 53 | 54 | hx = (ui >> 32) as u32; 55 | k = 0; 56 | if hx < 0x00100000 || (hx >> 31) > 0 { 57 | if ui << 1 == 0 { 58 | return -1. / (x * x); /* log(+-0)=-inf */ 59 | } 60 | if (hx >> 31) > 0 { 61 | return (x - x) / 0.0; /* log(-#) = NaN */ 62 | } 63 | /* subnormal number, scale x up */ 64 | k -= 54; 65 | x *= x1p54; 66 | ui = x.to_bits(); 67 | hx = (ui >> 32) as u32; 68 | } else if hx >= 0x7ff00000 { 69 | return x; 70 | } else if hx == 0x3ff00000 && ui << 32 == 0 { 71 | return 0.; 72 | } 73 | 74 | /* reduce x into [sqrt(2)/2, sqrt(2)] */ 75 | hx += 0x3ff00000 - 0x3fe6a09e; 76 | k += (hx >> 20) as i32 - 0x3ff; 77 | hx = (hx & 0x000fffff) + 0x3fe6a09e; 78 | ui = ((hx as u64) << 32) | (ui & 0xffffffff); 79 | x = f64::from_bits(ui); 80 | 81 | f = x - 1.0; 82 | hfsq = 0.5 * f * f; 83 | s = f / (2.0 + f); 84 | z = s * s; 85 | w = z * z; 86 | t1 = w * (LG2 + w * (LG4 + w * LG6)); 87 | t2 = z * (LG1 + w * (LG3 + w * (LG5 + w * LG7))); 88 | r = t2 + t1; 89 | 90 | /* hi+lo = f - hfsq + s*(hfsq+R) ~ log(1+f) */ 91 | hi = f - hfsq; 92 | ui = hi.to_bits(); 93 | ui &= (-1i64 as u64) << 32; 94 | hi = f64::from_bits(ui); 95 | lo = f - hi - hfsq + s * (hfsq + r); 96 | 97 | val_hi = hi * IVLN2HI; 98 | val_lo = (lo + hi) * IVLN2LO + lo * IVLN2HI; 99 | 100 | /* spadd(val_hi, val_lo, y), except for not using double_t: */ 101 | y = k.into(); 102 | w = y + val_hi; 103 | val_lo += (y - w) + val_hi; 104 | val_hi = w; 105 | 106 | val_lo + val_hi 107 | } 108 | -------------------------------------------------------------------------------- /libm/src/math/log2f.rs: -------------------------------------------------------------------------------- 1 | /* origin: FreeBSD /usr/src/lib/msun/src/e_log2f.c */ 2 | /* 3 | * ==================================================== 4 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 5 | * 6 | * Developed at SunPro, a Sun Microsystems, Inc. business. 7 | * Permission to use, copy, modify, and distribute this 8 | * software is freely granted, provided that this notice 9 | * is preserved. 10 | * ==================================================== 11 | */ 12 | /* 13 | * See comments in log2.c. 14 | */ 15 | 16 | use core::f32; 17 | 18 | const IVLN2HI: f32 = 1.4428710938e+00; /* 0x3fb8b000 */ 19 | const IVLN2LO: f32 = -1.7605285393e-04; /* 0xb9389ad4 */ 20 | /* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */ 21 | const LG1: f32 = 0.66666662693; /* 0xaaaaaa.0p-24 */ 22 | const LG2: f32 = 0.40000972152; /* 0xccce13.0p-25 */ 23 | const LG3: f32 = 0.28498786688; /* 0x91e9ee.0p-25 */ 24 | const LG4: f32 = 0.24279078841; /* 0xf89e26.0p-26 */ 25 | 26 | /// The base 2 logarithm of `x` (f32). 27 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 28 | pub fn log2f(mut x: f32) -> f32 { 29 | let x1p25f = f32::from_bits(0x4c000000); // 0x1p25f === 2 ^ 25 30 | 31 | let mut ui: u32 = x.to_bits(); 32 | let hfsq: f32; 33 | let f: f32; 34 | let s: f32; 35 | let z: f32; 36 | let r: f32; 37 | let w: f32; 38 | let t1: f32; 39 | let t2: f32; 40 | let mut hi: f32; 41 | let lo: f32; 42 | let mut ix: u32; 43 | let mut k: i32; 44 | 45 | ix = ui; 46 | k = 0; 47 | if ix < 0x00800000 || (ix >> 31) > 0 { 48 | /* x < 2**-126 */ 49 | if ix << 1 == 0 { 50 | return -1. / (x * x); /* log(+-0)=-inf */ 51 | } 52 | if (ix >> 31) > 0 { 53 | return (x - x) / 0.0; /* log(-#) = NaN */ 54 | } 55 | /* subnormal number, scale up x */ 56 | k -= 25; 57 | x *= x1p25f; 58 | ui = x.to_bits(); 59 | ix = ui; 60 | } else if ix >= 0x7f800000 { 61 | return x; 62 | } else if ix == 0x3f800000 { 63 | return 0.; 64 | } 65 | 66 | /* reduce x into [sqrt(2)/2, sqrt(2)] */ 67 | ix += 0x3f800000 - 0x3f3504f3; 68 | k += (ix >> 23) as i32 - 0x7f; 69 | ix = (ix & 0x007fffff) + 0x3f3504f3; 70 | ui = ix; 71 | x = f32::from_bits(ui); 72 | 73 | f = x - 1.0; 74 | s = f / (2.0 + f); 75 | z = s * s; 76 | w = z * z; 77 | t1 = w * (LG2 + w * LG4); 78 | t2 = z * (LG1 + w * LG3); 79 | r = t2 + t1; 80 | hfsq = 0.5 * f * f; 81 | 82 | hi = f - hfsq; 83 | ui = hi.to_bits(); 84 | ui &= 0xfffff000; 85 | hi = f32::from_bits(ui); 86 | lo = f - hi - hfsq + s * (hfsq + r); 87 | (lo + hi) * IVLN2LO + lo * IVLN2HI + hi * IVLN2HI + k as f32 88 | } 89 | -------------------------------------------------------------------------------- /libm/src/math/logf.rs: -------------------------------------------------------------------------------- 1 | /* origin: FreeBSD /usr/src/lib/msun/src/e_logf.c */ 2 | /* 3 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 4 | */ 5 | /* 6 | * ==================================================== 7 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 8 | * 9 | * Developed at SunPro, a Sun Microsystems, Inc. business. 10 | * Permission to use, copy, modify, and distribute this 11 | * software is freely granted, provided that this notice 12 | * is preserved. 13 | * ==================================================== 14 | */ 15 | 16 | const LN2_HI: f32 = 6.9313812256e-01; /* 0x3f317180 */ 17 | const LN2_LO: f32 = 9.0580006145e-06; /* 0x3717f7d1 */ 18 | /* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */ 19 | const LG1: f32 = 0.66666662693; /* 0xaaaaaa.0p-24*/ 20 | const LG2: f32 = 0.40000972152; /* 0xccce13.0p-25 */ 21 | const LG3: f32 = 0.28498786688; /* 0x91e9ee.0p-25 */ 22 | const LG4: f32 = 0.24279078841; /* 0xf89e26.0p-26 */ 23 | 24 | /// The natural logarithm of `x` (f32). 25 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 26 | pub fn logf(mut x: f32) -> f32 { 27 | let x1p25 = f32::from_bits(0x4c000000); // 0x1p25f === 2 ^ 25 28 | 29 | let mut ix = x.to_bits(); 30 | let mut k = 0i32; 31 | 32 | if (ix < 0x00800000) || ((ix >> 31) != 0) { 33 | /* x < 2**-126 */ 34 | if ix << 1 == 0 { 35 | return -1. / (x * x); /* log(+-0)=-inf */ 36 | } 37 | if (ix >> 31) != 0 { 38 | return (x - x) / 0.; /* log(-#) = NaN */ 39 | } 40 | /* subnormal number, scale up x */ 41 | k -= 25; 42 | x *= x1p25; 43 | ix = x.to_bits(); 44 | } else if ix >= 0x7f800000 { 45 | return x; 46 | } else if ix == 0x3f800000 { 47 | return 0.; 48 | } 49 | 50 | /* reduce x into [sqrt(2)/2, sqrt(2)] */ 51 | ix += 0x3f800000 - 0x3f3504f3; 52 | k += ((ix >> 23) as i32) - 0x7f; 53 | ix = (ix & 0x007fffff) + 0x3f3504f3; 54 | x = f32::from_bits(ix); 55 | 56 | let f = x - 1.; 57 | let s = f / (2. + f); 58 | let z = s * s; 59 | let w = z * z; 60 | let t1 = w * (LG2 + w * LG4); 61 | let t2 = z * (LG1 + w * LG3); 62 | let r = t2 + t1; 63 | let hfsq = 0.5 * f * f; 64 | let dk = k as f32; 65 | s * (hfsq + r) + dk * LN2_LO - hfsq + f + dk * LN2_HI 66 | } 67 | -------------------------------------------------------------------------------- /libm/src/math/modf.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 2 | pub fn modf(x: f64) -> (f64, f64) { 3 | let rv2: f64; 4 | let mut u = x.to_bits(); 5 | let mask: u64; 6 | let e = (((u >> 52) & 0x7ff) as i32) - 0x3ff; 7 | 8 | /* no fractional part */ 9 | if e >= 52 { 10 | rv2 = x; 11 | if e == 0x400 && (u << 12) != 0 { 12 | /* nan */ 13 | return (x, rv2); 14 | } 15 | u &= 1 << 63; 16 | return (f64::from_bits(u), rv2); 17 | } 18 | 19 | /* no integral part*/ 20 | if e < 0 { 21 | u &= 1 << 63; 22 | rv2 = f64::from_bits(u); 23 | return (x, rv2); 24 | } 25 | 26 | mask = ((!0) >> 12) >> e; 27 | if (u & mask) == 0 { 28 | rv2 = x; 29 | u &= 1 << 63; 30 | return (f64::from_bits(u), rv2); 31 | } 32 | u &= !mask; 33 | rv2 = f64::from_bits(u); 34 | return (x - rv2, rv2); 35 | } 36 | -------------------------------------------------------------------------------- /libm/src/math/modff.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 2 | pub fn modff(x: f32) -> (f32, f32) { 3 | let rv2: f32; 4 | let mut u: u32 = x.to_bits(); 5 | let mask: u32; 6 | let e = (((u >> 23) & 0xff) as i32) - 0x7f; 7 | 8 | /* no fractional part */ 9 | if e >= 23 { 10 | rv2 = x; 11 | if e == 0x80 && (u << 9) != 0 { 12 | /* nan */ 13 | return (x, rv2); 14 | } 15 | u &= 0x80000000; 16 | return (f32::from_bits(u), rv2); 17 | } 18 | /* no integral part */ 19 | if e < 0 { 20 | u &= 0x80000000; 21 | rv2 = f32::from_bits(u); 22 | return (x, rv2); 23 | } 24 | 25 | mask = 0x007fffff >> e; 26 | if (u & mask) == 0 { 27 | rv2 = x; 28 | u &= 0x80000000; 29 | return (f32::from_bits(u), rv2); 30 | } 31 | u &= !mask; 32 | rv2 = f32::from_bits(u); 33 | return (x - rv2, rv2); 34 | } 35 | -------------------------------------------------------------------------------- /libm/src/math/nextafter.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 2 | pub fn nextafter(x: f64, y: f64) -> f64 { 3 | if x.is_nan() || y.is_nan() { 4 | return x + y; 5 | } 6 | 7 | let mut ux_i = x.to_bits(); 8 | let uy_i = y.to_bits(); 9 | if ux_i == uy_i { 10 | return y; 11 | } 12 | 13 | let ax = ux_i & (!1_u64 / 2); 14 | let ay = uy_i & (!1_u64 / 2); 15 | if ax == 0 { 16 | if ay == 0 { 17 | return y; 18 | } 19 | ux_i = (uy_i & (1_u64 << 63)) | 1; 20 | } else if ax > ay || ((ux_i ^ uy_i) & (1_u64 << 63)) != 0 { 21 | ux_i -= 1; 22 | } else { 23 | ux_i += 1; 24 | } 25 | 26 | let e = (ux_i >> 52) & 0x7ff; 27 | // raise overflow if ux.f is infinite and x is finite 28 | if e == 0x7ff { 29 | force_eval!(x + x); 30 | } 31 | let ux_f = f64::from_bits(ux_i); 32 | // raise underflow if ux.f is subnormal or zero 33 | if e == 0 { 34 | force_eval!(x * x + ux_f * ux_f); 35 | } 36 | ux_f 37 | } 38 | -------------------------------------------------------------------------------- /libm/src/math/nextafterf.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 2 | pub fn nextafterf(x: f32, y: f32) -> f32 { 3 | if x.is_nan() || y.is_nan() { 4 | return x + y; 5 | } 6 | 7 | let mut ux_i = x.to_bits(); 8 | let uy_i = y.to_bits(); 9 | if ux_i == uy_i { 10 | return y; 11 | } 12 | 13 | let ax = ux_i & 0x7fff_ffff_u32; 14 | let ay = uy_i & 0x7fff_ffff_u32; 15 | if ax == 0 { 16 | if ay == 0 { 17 | return y; 18 | } 19 | ux_i = (uy_i & 0x8000_0000_u32) | 1; 20 | } else if ax > ay || ((ux_i ^ uy_i) & 0x8000_0000_u32) != 0 { 21 | ux_i -= 1; 22 | } else { 23 | ux_i += 1; 24 | } 25 | 26 | let e = ux_i & 0x7f80_0000_u32; 27 | // raise overflow if ux_f is infinite and x is finite 28 | if e == 0x7f80_0000_u32 { 29 | force_eval!(x + x); 30 | } 31 | let ux_f = f32::from_bits(ux_i); 32 | // raise underflow if ux_f is subnormal or zero 33 | if e == 0 { 34 | force_eval!(x * x + ux_f * ux_f); 35 | } 36 | ux_f 37 | } 38 | -------------------------------------------------------------------------------- /libm/src/math/rem_pio2f.rs: -------------------------------------------------------------------------------- 1 | /* origin: FreeBSD /usr/src/lib/msun/src/e_rem_pio2f.c */ 2 | /* 3 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 4 | * Debugged and optimized by Bruce D. Evans. 5 | */ 6 | /* 7 | * ==================================================== 8 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 9 | * 10 | * Developed at SunPro, a Sun Microsystems, Inc. business. 11 | * Permission to use, copy, modify, and distribute this 12 | * software is freely granted, provided that this notice 13 | * is preserved. 14 | * ==================================================== 15 | */ 16 | 17 | use core::f64; 18 | 19 | use super::rem_pio2_large; 20 | 21 | const TOINT: f64 = 1.5 / f64::EPSILON; 22 | 23 | /// 53 bits of 2/pi 24 | const INV_PIO2: f64 = 6.36619772367581382433e-01; /* 0x3FE45F30, 0x6DC9C883 */ 25 | /// first 25 bits of pi/2 26 | const PIO2_1: f64 = 1.57079631090164184570e+00; /* 0x3FF921FB, 0x50000000 */ 27 | /// pi/2 - pio2_1 28 | const PIO2_1T: f64 = 1.58932547735281966916e-08; /* 0x3E5110b4, 0x611A6263 */ 29 | 30 | /// Return the remainder of x rem pi/2 in *y 31 | /// 32 | /// use double precision for everything except passing x 33 | /// use __rem_pio2_large() for large x 34 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 35 | pub(crate) fn rem_pio2f(x: f32) -> (i32, f64) { 36 | let x64 = x as f64; 37 | 38 | let mut tx: [f64; 1] = [0.]; 39 | let mut ty: [f64; 1] = [0.]; 40 | 41 | let ix = x.to_bits() & 0x7fffffff; 42 | /* 25+53 bit pi is good enough for medium size */ 43 | if ix < 0x4dc90fdb { 44 | /* |x| ~< 2^28*(pi/2), medium size */ 45 | /* Use a specialized rint() to get fn. Assume round-to-nearest. */ 46 | let tmp = x64 * INV_PIO2 + TOINT; 47 | // force rounding of tmp to it's storage format on x87 to avoid 48 | // excess precision issues. 49 | #[cfg(all(target_arch = "x86", not(target_feature = "sse2")))] 50 | let tmp = force_eval!(tmp); 51 | let f_n = tmp - TOINT; 52 | return (f_n as i32, x64 - f_n * PIO2_1 - f_n * PIO2_1T); 53 | } 54 | if ix >= 0x7f800000 { 55 | /* x is inf or NaN */ 56 | return (0, x64 - x64); 57 | } 58 | /* scale x into [2^23, 2^24-1] */ 59 | let sign = (x.to_bits() >> 31) != 0; 60 | let e0 = ((ix >> 23) - (0x7f + 23)) as i32; /* e0 = ilogb(|x|)-23, positive */ 61 | tx[0] = f32::from_bits(ix - (e0 << 23) as u32) as f64; 62 | let n = rem_pio2_large(&tx, &mut ty, e0, 0); 63 | if sign { 64 | return (-n, -ty[0]); 65 | } 66 | (n, ty[0]) 67 | } 68 | -------------------------------------------------------------------------------- /libm/src/math/remainder.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 2 | pub fn remainder(x: f64, y: f64) -> f64 { 3 | let (result, _) = super::remquo(x, y); 4 | result 5 | } 6 | -------------------------------------------------------------------------------- /libm/src/math/remainderf.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 2 | pub fn remainderf(x: f32, y: f32) -> f32 { 3 | let (result, _) = super::remquof(x, y); 4 | result 5 | } 6 | -------------------------------------------------------------------------------- /libm/src/math/remquo.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 2 | pub fn remquo(mut x: f64, mut y: f64) -> (f64, i32) { 3 | let ux: u64 = x.to_bits(); 4 | let mut uy: u64 = y.to_bits(); 5 | let mut ex = ((ux >> 52) & 0x7ff) as i32; 6 | let mut ey = ((uy >> 52) & 0x7ff) as i32; 7 | let sx = (ux >> 63) != 0; 8 | let sy = (uy >> 63) != 0; 9 | let mut q: u32; 10 | let mut i: u64; 11 | let mut uxi: u64 = ux; 12 | 13 | if (uy << 1) == 0 || y.is_nan() || ex == 0x7ff { 14 | return ((x * y) / (x * y), 0); 15 | } 16 | if (ux << 1) == 0 { 17 | return (x, 0); 18 | } 19 | 20 | /* normalize x and y */ 21 | if ex == 0 { 22 | i = uxi << 12; 23 | while (i >> 63) == 0 { 24 | ex -= 1; 25 | i <<= 1; 26 | } 27 | uxi <<= -ex + 1; 28 | } else { 29 | uxi &= (!0) >> 12; 30 | uxi |= 1 << 52; 31 | } 32 | if ey == 0 { 33 | i = uy << 12; 34 | while (i >> 63) == 0 { 35 | ey -= 1; 36 | i <<= 1; 37 | } 38 | uy <<= -ey + 1; 39 | } else { 40 | uy &= (!0) >> 12; 41 | uy |= 1 << 52; 42 | } 43 | 44 | q = 0; 45 | 46 | if ex + 1 != ey { 47 | if ex < ey { 48 | return (x, 0); 49 | } 50 | /* x mod y */ 51 | while ex > ey { 52 | i = uxi.wrapping_sub(uy); 53 | if (i >> 63) == 0 { 54 | uxi = i; 55 | q += 1; 56 | } 57 | uxi <<= 1; 58 | q <<= 1; 59 | ex -= 1; 60 | } 61 | i = uxi.wrapping_sub(uy); 62 | if (i >> 63) == 0 { 63 | uxi = i; 64 | q += 1; 65 | } 66 | if uxi == 0 { 67 | ex = -60; 68 | } else { 69 | while (uxi >> 52) == 0 { 70 | uxi <<= 1; 71 | ex -= 1; 72 | } 73 | } 74 | } 75 | 76 | /* scale result and decide between |x| and |x|-|y| */ 77 | if ex > 0 { 78 | uxi -= 1 << 52; 79 | uxi |= (ex as u64) << 52; 80 | } else { 81 | uxi >>= -ex + 1; 82 | } 83 | x = f64::from_bits(uxi); 84 | if sy { 85 | y = -y; 86 | } 87 | if ex == ey || (ex + 1 == ey && (2.0 * x > y || (2.0 * x == y && (q % 2) != 0))) { 88 | x -= y; 89 | // TODO: this matches musl behavior, but it is incorrect 90 | q = q.wrapping_add(1); 91 | } 92 | q &= 0x7fffffff; 93 | let quo = if sx ^ sy { -(q as i32) } else { q as i32 }; 94 | if sx { (-x, quo) } else { (x, quo) } 95 | } 96 | 97 | #[cfg(test)] 98 | mod tests { 99 | use super::remquo; 100 | 101 | #[test] 102 | fn test_q_overflow() { 103 | // 0xc000000000000001, 0x04c0000000000004 104 | let _ = remquo(-2.0000000000000004, 8.406091369059082e-286); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /libm/src/math/remquof.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 2 | pub fn remquof(mut x: f32, mut y: f32) -> (f32, i32) { 3 | let ux: u32 = x.to_bits(); 4 | let mut uy: u32 = y.to_bits(); 5 | let mut ex = ((ux >> 23) & 0xff) as i32; 6 | let mut ey = ((uy >> 23) & 0xff) as i32; 7 | let sx = (ux >> 31) != 0; 8 | let sy = (uy >> 31) != 0; 9 | let mut q: u32; 10 | let mut i: u32; 11 | let mut uxi: u32 = ux; 12 | 13 | if (uy << 1) == 0 || y.is_nan() || ex == 0xff { 14 | return ((x * y) / (x * y), 0); 15 | } 16 | if (ux << 1) == 0 { 17 | return (x, 0); 18 | } 19 | 20 | /* normalize x and y */ 21 | if ex == 0 { 22 | i = uxi << 9; 23 | while (i >> 31) == 0 { 24 | ex -= 1; 25 | i <<= 1; 26 | } 27 | uxi <<= -ex + 1; 28 | } else { 29 | uxi &= (!0) >> 9; 30 | uxi |= 1 << 23; 31 | } 32 | if ey == 0 { 33 | i = uy << 9; 34 | while (i >> 31) == 0 { 35 | ey -= 1; 36 | i <<= 1; 37 | } 38 | uy <<= -ey + 1; 39 | } else { 40 | uy &= (!0) >> 9; 41 | uy |= 1 << 23; 42 | } 43 | 44 | q = 0; 45 | if ex + 1 != ey { 46 | if ex < ey { 47 | return (x, 0); 48 | } 49 | /* x mod y */ 50 | while ex > ey { 51 | i = uxi.wrapping_sub(uy); 52 | if (i >> 31) == 0 { 53 | uxi = i; 54 | q += 1; 55 | } 56 | uxi <<= 1; 57 | q <<= 1; 58 | ex -= 1; 59 | } 60 | i = uxi.wrapping_sub(uy); 61 | if (i >> 31) == 0 { 62 | uxi = i; 63 | q += 1; 64 | } 65 | if uxi == 0 { 66 | ex = -30; 67 | } else { 68 | while (uxi >> 23) == 0 { 69 | uxi <<= 1; 70 | ex -= 1; 71 | } 72 | } 73 | } 74 | 75 | /* scale result and decide between |x| and |x|-|y| */ 76 | if ex > 0 { 77 | uxi -= 1 << 23; 78 | uxi |= (ex as u32) << 23; 79 | } else { 80 | uxi >>= -ex + 1; 81 | } 82 | x = f32::from_bits(uxi); 83 | if sy { 84 | y = -y; 85 | } 86 | if ex == ey || (ex + 1 == ey && (2.0 * x > y || (2.0 * x == y && (q % 2) != 0))) { 87 | x -= y; 88 | q += 1; 89 | } 90 | q &= 0x7fffffff; 91 | let quo = if sx ^ sy { -(q as i32) } else { q as i32 }; 92 | if sx { (-x, quo) } else { (x, quo) } 93 | } 94 | -------------------------------------------------------------------------------- /libm/src/math/rint.rs: -------------------------------------------------------------------------------- 1 | use super::support::Round; 2 | 3 | /// Round `x` to the nearest integer, breaking ties toward even. 4 | #[cfg(f16_enabled)] 5 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 6 | pub fn rintf16(x: f16) -> f16 { 7 | select_implementation! { 8 | name: rintf16, 9 | use_arch: all(target_arch = "aarch64", target_feature = "fp16"), 10 | args: x, 11 | } 12 | 13 | super::generic::rint_round(x, Round::Nearest).val 14 | } 15 | 16 | /// Round `x` to the nearest integer, breaking ties toward even. 17 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 18 | pub fn rintf(x: f32) -> f32 { 19 | select_implementation! { 20 | name: rintf, 21 | use_arch: any( 22 | all(target_arch = "aarch64", target_feature = "neon"), 23 | all(target_arch = "wasm32", intrinsics_enabled), 24 | ), 25 | args: x, 26 | } 27 | 28 | super::generic::rint_round(x, Round::Nearest).val 29 | } 30 | 31 | /// Round `x` to the nearest integer, breaking ties toward even. 32 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 33 | pub fn rint(x: f64) -> f64 { 34 | select_implementation! { 35 | name: rint, 36 | use_arch: any( 37 | all(target_arch = "aarch64", target_feature = "neon"), 38 | all(target_arch = "wasm32", intrinsics_enabled), 39 | ), 40 | args: x, 41 | } 42 | 43 | super::generic::rint_round(x, Round::Nearest).val 44 | } 45 | 46 | /// Round `x` to the nearest integer, breaking ties toward even. 47 | #[cfg(f128_enabled)] 48 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 49 | pub fn rintf128(x: f128) -> f128 { 50 | super::generic::rint_round(x, Round::Nearest).val 51 | } 52 | -------------------------------------------------------------------------------- /libm/src/math/round.rs: -------------------------------------------------------------------------------- 1 | /// Round `x` to the nearest integer, breaking ties away from zero. 2 | #[cfg(f16_enabled)] 3 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 4 | pub fn roundf16(x: f16) -> f16 { 5 | super::generic::round(x) 6 | } 7 | 8 | /// Round `x` to the nearest integer, breaking ties away from zero. 9 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 10 | pub fn roundf(x: f32) -> f32 { 11 | super::generic::round(x) 12 | } 13 | 14 | /// Round `x` to the nearest integer, breaking ties away from zero. 15 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 16 | pub fn round(x: f64) -> f64 { 17 | super::generic::round(x) 18 | } 19 | 20 | /// Round `x` to the nearest integer, breaking ties away from zero. 21 | #[cfg(f128_enabled)] 22 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 23 | pub fn roundf128(x: f128) -> f128 { 24 | super::generic::round(x) 25 | } 26 | -------------------------------------------------------------------------------- /libm/src/math/roundeven.rs: -------------------------------------------------------------------------------- 1 | use super::support::{Float, Round}; 2 | 3 | /// Round `x` to the nearest integer, breaking ties toward even. This is IEEE 754 4 | /// `roundToIntegralTiesToEven`. 5 | #[cfg(f16_enabled)] 6 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 7 | pub fn roundevenf16(x: f16) -> f16 { 8 | roundeven_impl(x) 9 | } 10 | 11 | /// Round `x` to the nearest integer, breaking ties toward even. This is IEEE 754 12 | /// `roundToIntegralTiesToEven`. 13 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 14 | pub fn roundevenf(x: f32) -> f32 { 15 | roundeven_impl(x) 16 | } 17 | 18 | /// Round `x` to the nearest integer, breaking ties toward even. This is IEEE 754 19 | /// `roundToIntegralTiesToEven`. 20 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 21 | pub fn roundeven(x: f64) -> f64 { 22 | roundeven_impl(x) 23 | } 24 | 25 | /// Round `x` to the nearest integer, breaking ties toward even. This is IEEE 754 26 | /// `roundToIntegralTiesToEven`. 27 | #[cfg(f128_enabled)] 28 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 29 | pub fn roundevenf128(x: f128) -> f128 { 30 | roundeven_impl(x) 31 | } 32 | 33 | #[inline] 34 | pub fn roundeven_impl(x: F) -> F { 35 | super::generic::rint_round(x, Round::Nearest).val 36 | } 37 | -------------------------------------------------------------------------------- /libm/src/math/roundf.rs: -------------------------------------------------------------------------------- 1 | /// Round `x` to the nearest integer, breaking ties away from zero. 2 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 3 | pub fn roundf(x: f32) -> f32 { 4 | super::generic::round(x) 5 | } 6 | -------------------------------------------------------------------------------- /libm/src/math/roundf128.rs: -------------------------------------------------------------------------------- 1 | /// Round `x` to the nearest integer, breaking ties away from zero. 2 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 3 | pub fn roundf128(x: f128) -> f128 { 4 | super::generic::round(x) 5 | } 6 | -------------------------------------------------------------------------------- /libm/src/math/roundf16.rs: -------------------------------------------------------------------------------- 1 | /// Round `x` to the nearest integer, breaking ties away from zero. 2 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 3 | pub fn roundf16(x: f16) -> f16 { 4 | super::generic::round(x) 5 | } 6 | -------------------------------------------------------------------------------- /libm/src/math/scalbn.rs: -------------------------------------------------------------------------------- 1 | #[cfg(f16_enabled)] 2 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 3 | pub fn scalbnf16(x: f16, n: i32) -> f16 { 4 | super::generic::scalbn(x, n) 5 | } 6 | 7 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 8 | pub fn scalbnf(x: f32, n: i32) -> f32 { 9 | super::generic::scalbn(x, n) 10 | } 11 | 12 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 13 | pub fn scalbn(x: f64, n: i32) -> f64 { 14 | super::generic::scalbn(x, n) 15 | } 16 | 17 | #[cfg(f128_enabled)] 18 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 19 | pub fn scalbnf128(x: f128, n: i32) -> f128 { 20 | super::generic::scalbn(x, n) 21 | } 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use super::*; 26 | use crate::support::{CastFrom, CastInto, Float}; 27 | 28 | // Tests against N3220 29 | fn spec_test(f: impl Fn(F, i32) -> F) 30 | where 31 | u32: CastInto, 32 | F::Int: CastFrom, 33 | F::Int: CastFrom, 34 | { 35 | // `scalbn(±0, n)` returns `±0`. 36 | assert_biteq!(f(F::NEG_ZERO, 10), F::NEG_ZERO); 37 | assert_biteq!(f(F::NEG_ZERO, 0), F::NEG_ZERO); 38 | assert_biteq!(f(F::NEG_ZERO, -10), F::NEG_ZERO); 39 | assert_biteq!(f(F::ZERO, 10), F::ZERO); 40 | assert_biteq!(f(F::ZERO, 0), F::ZERO); 41 | assert_biteq!(f(F::ZERO, -10), F::ZERO); 42 | 43 | // `scalbn(x, 0)` returns `x`. 44 | assert_biteq!(f(F::MIN, 0), F::MIN); 45 | assert_biteq!(f(F::MAX, 0), F::MAX); 46 | assert_biteq!(f(F::INFINITY, 0), F::INFINITY); 47 | assert_biteq!(f(F::NEG_INFINITY, 0), F::NEG_INFINITY); 48 | assert_biteq!(f(F::ZERO, 0), F::ZERO); 49 | assert_biteq!(f(F::NEG_ZERO, 0), F::NEG_ZERO); 50 | 51 | // `scalbn(±∞, n)` returns `±∞`. 52 | assert_biteq!(f(F::INFINITY, 10), F::INFINITY); 53 | assert_biteq!(f(F::INFINITY, -10), F::INFINITY); 54 | assert_biteq!(f(F::NEG_INFINITY, 10), F::NEG_INFINITY); 55 | assert_biteq!(f(F::NEG_INFINITY, -10), F::NEG_INFINITY); 56 | 57 | // NaN should remain NaNs. 58 | assert!(f(F::NAN, 10).is_nan()); 59 | assert!(f(F::NAN, 0).is_nan()); 60 | assert!(f(F::NAN, -10).is_nan()); 61 | assert!(f(-F::NAN, 10).is_nan()); 62 | assert!(f(-F::NAN, 0).is_nan()); 63 | assert!(f(-F::NAN, -10).is_nan()); 64 | } 65 | 66 | #[test] 67 | #[cfg(f16_enabled)] 68 | fn spec_test_f16() { 69 | spec_test::(scalbnf16); 70 | } 71 | 72 | #[test] 73 | fn spec_test_f32() { 74 | spec_test::(scalbnf); 75 | } 76 | 77 | #[test] 78 | fn spec_test_f64() { 79 | spec_test::(scalbn); 80 | } 81 | 82 | #[test] 83 | #[cfg(f128_enabled)] 84 | fn spec_test_f128() { 85 | spec_test::(scalbnf128); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /libm/src/math/scalbnf.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 2 | pub fn scalbnf(x: f32, n: i32) -> f32 { 3 | super::generic::scalbn(x, n) 4 | } 5 | -------------------------------------------------------------------------------- /libm/src/math/scalbnf128.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 2 | pub fn scalbnf128(x: f128, n: i32) -> f128 { 3 | super::generic::scalbn(x, n) 4 | } 5 | -------------------------------------------------------------------------------- /libm/src/math/scalbnf16.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 2 | pub fn scalbnf16(x: f16, n: i32) -> f16 { 3 | super::generic::scalbn(x, n) 4 | } 5 | -------------------------------------------------------------------------------- /libm/src/math/sin.rs: -------------------------------------------------------------------------------- 1 | // origin: FreeBSD /usr/src/lib/msun/src/s_sin.c */ 2 | // 3 | // ==================================================== 4 | // Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 5 | // 6 | // Developed at SunPro, a Sun Microsystems, Inc. business. 7 | // Permission to use, copy, modify, and distribute this 8 | // software is freely granted, provided that this notice 9 | // is preserved. 10 | // ==================================================== 11 | 12 | use super::{k_cos, k_sin, rem_pio2}; 13 | 14 | // sin(x) 15 | // Return sine function of x. 16 | // 17 | // kernel function: 18 | // k_sin ... sine function on [-pi/4,pi/4] 19 | // k_cos ... cose function on [-pi/4,pi/4] 20 | // rem_pio2 ... argument reduction routine 21 | // 22 | // Method. 23 | // Let S,C and T denote the sin, cos and tan respectively on 24 | // [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 25 | // in [-pi/4 , +pi/4], and let n = k mod 4. 26 | // We have 27 | // 28 | // n sin(x) cos(x) tan(x) 29 | // ---------------------------------------------------------- 30 | // 0 S C T 31 | // 1 C -S -1/T 32 | // 2 -S -C T 33 | // 3 -C S -1/T 34 | // ---------------------------------------------------------- 35 | // 36 | // Special cases: 37 | // Let trig be any of sin, cos, or tan. 38 | // trig(+-INF) is NaN, with signals; 39 | // trig(NaN) is that NaN; 40 | // 41 | // Accuracy: 42 | // TRIG(x) returns trig(x) nearly rounded 43 | 44 | /// The sine of `x` (f64). 45 | /// 46 | /// `x` is specified in radians. 47 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 48 | pub fn sin(x: f64) -> f64 { 49 | let x1p120 = f64::from_bits(0x4770000000000000); // 0x1p120f === 2 ^ 120 50 | 51 | /* High word of x. */ 52 | let ix = (f64::to_bits(x) >> 32) as u32 & 0x7fffffff; 53 | 54 | /* |x| ~< pi/4 */ 55 | if ix <= 0x3fe921fb { 56 | if ix < 0x3e500000 { 57 | /* |x| < 2**-26 */ 58 | /* raise inexact if x != 0 and underflow if subnormal*/ 59 | if ix < 0x00100000 { 60 | force_eval!(x / x1p120); 61 | } else { 62 | force_eval!(x + x1p120); 63 | } 64 | return x; 65 | } 66 | return k_sin(x, 0.0, 0); 67 | } 68 | 69 | /* sin(Inf or NaN) is NaN */ 70 | if ix >= 0x7ff00000 { 71 | return x - x; 72 | } 73 | 74 | /* argument reduction needed */ 75 | let (n, y0, y1) = rem_pio2(x); 76 | match n & 3 { 77 | 0 => k_sin(y0, y1, 1), 78 | 1 => k_cos(y0, y1), 79 | 2 => -k_sin(y0, y1, 1), 80 | _ => -k_cos(y0, y1), 81 | } 82 | } 83 | 84 | #[cfg(test)] 85 | mod tests { 86 | use super::*; 87 | 88 | #[test] 89 | #[cfg_attr(x86_no_sse, ignore = "FIXME(i586): possible incorrect rounding")] 90 | fn test_near_pi() { 91 | let x = f64::from_bits(0x400921fb000FD5DD); // 3.141592026217707 92 | let sx = f64::from_bits(0x3ea50d15ced1a4a2); // 6.273720864039205e-7 93 | assert_eq!(sin(x), sx); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /libm/src/math/sinf.rs: -------------------------------------------------------------------------------- 1 | /* origin: FreeBSD /usr/src/lib/msun/src/s_sinf.c */ 2 | /* 3 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 4 | * Optimized by Bruce D. Evans. 5 | */ 6 | /* 7 | * ==================================================== 8 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 9 | * 10 | * Developed at SunPro, a Sun Microsystems, Inc. business. 11 | * Permission to use, copy, modify, and distribute this 12 | * software is freely granted, provided that this notice 13 | * is preserved. 14 | * ==================================================== 15 | */ 16 | 17 | use core::f64::consts::FRAC_PI_2; 18 | 19 | use super::{k_cosf, k_sinf, rem_pio2f}; 20 | 21 | /* Small multiples of pi/2 rounded to double precision. */ 22 | const S1_PIO2: f64 = 1. * FRAC_PI_2; /* 0x3FF921FB, 0x54442D18 */ 23 | const S2_PIO2: f64 = 2. * FRAC_PI_2; /* 0x400921FB, 0x54442D18 */ 24 | const S3_PIO2: f64 = 3. * FRAC_PI_2; /* 0x4012D97C, 0x7F3321D2 */ 25 | const S4_PIO2: f64 = 4. * FRAC_PI_2; /* 0x401921FB, 0x54442D18 */ 26 | 27 | /// The sine of `x` (f32). 28 | /// 29 | /// `x` is specified in radians. 30 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 31 | pub fn sinf(x: f32) -> f32 { 32 | let x64 = x as f64; 33 | 34 | let x1p120 = f32::from_bits(0x7b800000); // 0x1p120f === 2 ^ 120 35 | 36 | let mut ix = x.to_bits(); 37 | let sign = (ix >> 31) != 0; 38 | ix &= 0x7fffffff; 39 | 40 | if ix <= 0x3f490fda { 41 | /* |x| ~<= pi/4 */ 42 | if ix < 0x39800000 { 43 | /* |x| < 2**-12 */ 44 | /* raise inexact if x!=0 and underflow if subnormal */ 45 | force_eval!(if ix < 0x00800000 { x / x1p120 } else { x + x1p120 }); 46 | return x; 47 | } 48 | return k_sinf(x64); 49 | } 50 | if ix <= 0x407b53d1 { 51 | /* |x| ~<= 5*pi/4 */ 52 | if ix <= 0x4016cbe3 { 53 | /* |x| ~<= 3pi/4 */ 54 | if sign { 55 | return -k_cosf(x64 + S1_PIO2); 56 | } else { 57 | return k_cosf(x64 - S1_PIO2); 58 | } 59 | } 60 | return k_sinf(if sign { -(x64 + S2_PIO2) } else { -(x64 - S2_PIO2) }); 61 | } 62 | if ix <= 0x40e231d5 { 63 | /* |x| ~<= 9*pi/4 */ 64 | if ix <= 0x40afeddf { 65 | /* |x| ~<= 7*pi/4 */ 66 | if sign { 67 | return k_cosf(x64 + S3_PIO2); 68 | } else { 69 | return -k_cosf(x64 - S3_PIO2); 70 | } 71 | } 72 | return k_sinf(if sign { x64 + S4_PIO2 } else { x64 - S4_PIO2 }); 73 | } 74 | 75 | /* sin(Inf or NaN) is NaN */ 76 | if ix >= 0x7f800000 { 77 | return x - x; 78 | } 79 | 80 | /* general argument reduction needed */ 81 | let (n, y) = rem_pio2f(x); 82 | match n & 3 { 83 | 0 => k_sinf(y), 84 | 1 => k_cosf(y), 85 | 2 => k_sinf(-y), 86 | _ => -k_cosf(y), 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /libm/src/math/sinh.rs: -------------------------------------------------------------------------------- 1 | use super::{expm1, expo2}; 2 | 3 | // sinh(x) = (exp(x) - 1/exp(x))/2 4 | // = (exp(x)-1 + (exp(x)-1)/exp(x))/2 5 | // = x + x^3/6 + o(x^5) 6 | // 7 | 8 | /// The hyperbolic sine of `x` (f64). 9 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 10 | pub fn sinh(x: f64) -> f64 { 11 | // union {double f; uint64_t i;} u = {.f = x}; 12 | // uint32_t w; 13 | // double t, h, absx; 14 | 15 | let mut uf: f64 = x; 16 | let mut ui: u64 = f64::to_bits(uf); 17 | let w: u32; 18 | let t: f64; 19 | let mut h: f64; 20 | let absx: f64; 21 | 22 | h = 0.5; 23 | if ui >> 63 != 0 { 24 | h = -h; 25 | } 26 | /* |x| */ 27 | ui &= !1 / 2; 28 | uf = f64::from_bits(ui); 29 | absx = uf; 30 | w = (ui >> 32) as u32; 31 | 32 | /* |x| < log(DBL_MAX) */ 33 | if w < 0x40862e42 { 34 | t = expm1(absx); 35 | if w < 0x3ff00000 { 36 | if w < 0x3ff00000 - (26 << 20) { 37 | /* note: inexact and underflow are raised by expm1 */ 38 | /* note: this branch avoids spurious underflow */ 39 | return x; 40 | } 41 | return h * (2.0 * t - t * t / (t + 1.0)); 42 | } 43 | /* note: |x|>log(0x1p26)+eps could be just h*exp(x) */ 44 | return h * (t + t / (t + 1.0)); 45 | } 46 | 47 | /* |x| > log(DBL_MAX) or nan */ 48 | /* note: the result is stored to handle overflow */ 49 | t = 2.0 * h * expo2(absx); 50 | t 51 | } 52 | -------------------------------------------------------------------------------- /libm/src/math/sinhf.rs: -------------------------------------------------------------------------------- 1 | use super::{expm1f, k_expo2f}; 2 | 3 | /// The hyperbolic sine of `x` (f32). 4 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 5 | pub fn sinhf(x: f32) -> f32 { 6 | let mut h = 0.5f32; 7 | let mut ix = x.to_bits(); 8 | if (ix >> 31) != 0 { 9 | h = -h; 10 | } 11 | /* |x| */ 12 | ix &= 0x7fffffff; 13 | let absx = f32::from_bits(ix); 14 | let w = ix; 15 | 16 | /* |x| < log(FLT_MAX) */ 17 | if w < 0x42b17217 { 18 | let t = expm1f(absx); 19 | if w < 0x3f800000 { 20 | if w < (0x3f800000 - (12 << 23)) { 21 | return x; 22 | } 23 | return h * (2. * t - t * t / (t + 1.)); 24 | } 25 | return h * (t + t / (t + 1.)); 26 | } 27 | 28 | /* |x| > logf(FLT_MAX) or nan */ 29 | 2. * h * k_expo2f(absx) 30 | } 31 | -------------------------------------------------------------------------------- /libm/src/math/sqrt.rs: -------------------------------------------------------------------------------- 1 | /// The square root of `x` (f16). 2 | #[cfg(f16_enabled)] 3 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 4 | pub fn sqrtf16(x: f16) -> f16 { 5 | select_implementation! { 6 | name: sqrtf16, 7 | use_arch: all(target_arch = "aarch64", target_feature = "fp16"), 8 | args: x, 9 | } 10 | 11 | return super::generic::sqrt(x); 12 | } 13 | 14 | /// The square root of `x` (f32). 15 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 16 | pub fn sqrtf(x: f32) -> f32 { 17 | select_implementation! { 18 | name: sqrtf, 19 | use_arch: any( 20 | all(target_arch = "aarch64", target_feature = "neon"), 21 | all(target_arch = "wasm32", intrinsics_enabled), 22 | target_feature = "sse2" 23 | ), 24 | args: x, 25 | } 26 | 27 | super::generic::sqrt(x) 28 | } 29 | 30 | /// The square root of `x` (f64). 31 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 32 | pub fn sqrt(x: f64) -> f64 { 33 | select_implementation! { 34 | name: sqrt, 35 | use_arch: any( 36 | all(target_arch = "aarch64", target_feature = "neon"), 37 | all(target_arch = "wasm32", intrinsics_enabled), 38 | target_feature = "sse2" 39 | ), 40 | args: x, 41 | } 42 | 43 | super::generic::sqrt(x) 44 | } 45 | 46 | /// The square root of `x` (f128). 47 | #[cfg(f128_enabled)] 48 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 49 | pub fn sqrtf128(x: f128) -> f128 { 50 | return super::generic::sqrt(x); 51 | } 52 | -------------------------------------------------------------------------------- /libm/src/math/sqrtf.rs: -------------------------------------------------------------------------------- 1 | /// The square root of `x` (f32). 2 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 3 | pub fn sqrtf(x: f32) -> f32 { 4 | select_implementation! { 5 | name: sqrtf, 6 | use_arch: any( 7 | all(target_arch = "aarch64", target_feature = "neon"), 8 | all(target_arch = "wasm32", intrinsics_enabled), 9 | target_feature = "sse2" 10 | ), 11 | args: x, 12 | } 13 | 14 | super::generic::sqrt(x) 15 | } 16 | -------------------------------------------------------------------------------- /libm/src/math/sqrtf128.rs: -------------------------------------------------------------------------------- 1 | /// The square root of `x` (f128). 2 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 3 | pub fn sqrtf128(x: f128) -> f128 { 4 | return super::generic::sqrt(x); 5 | } 6 | -------------------------------------------------------------------------------- /libm/src/math/sqrtf16.rs: -------------------------------------------------------------------------------- 1 | /// The square root of `x` (f16). 2 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 3 | pub fn sqrtf16(x: f16) -> f16 { 4 | select_implementation! { 5 | name: sqrtf16, 6 | use_arch: all(target_arch = "aarch64", target_feature = "fp16"), 7 | args: x, 8 | } 9 | 10 | return super::generic::sqrt(x); 11 | } 12 | -------------------------------------------------------------------------------- /libm/src/math/support/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | pub mod macros; 3 | mod big; 4 | mod env; 5 | mod float_traits; 6 | pub mod hex_float; 7 | mod int_traits; 8 | 9 | #[allow(unused_imports)] 10 | pub use big::{i256, u256}; 11 | pub use env::{FpResult, Round, Status}; 12 | #[allow(unused_imports)] 13 | pub use float_traits::{DFloat, Float, HFloat, IntTy}; 14 | pub(crate) use float_traits::{f32_from_bits, f64_from_bits}; 15 | #[cfg(f16_enabled)] 16 | #[allow(unused_imports)] 17 | pub use hex_float::hf16; 18 | #[cfg(f128_enabled)] 19 | #[allow(unused_imports)] 20 | pub use hex_float::hf128; 21 | #[allow(unused_imports)] 22 | pub use hex_float::{Hexf, hf32, hf64}; 23 | pub use int_traits::{CastFrom, CastInto, DInt, HInt, Int, MinInt}; 24 | 25 | /// Hint to the compiler that the current path is cold. 26 | pub fn cold_path() { 27 | #[cfg(intrinsics_enabled)] 28 | core::intrinsics::cold_path(); 29 | } 30 | -------------------------------------------------------------------------------- /libm/src/math/tan.rs: -------------------------------------------------------------------------------- 1 | // origin: FreeBSD /usr/src/lib/msun/src/s_tan.c */ 2 | // 3 | // ==================================================== 4 | // Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 5 | // 6 | // Developed at SunPro, a Sun Microsystems, Inc. business. 7 | // Permission to use, copy, modify, and distribute this 8 | // software is freely granted, provided that this notice 9 | // is preserved. 10 | // ==================================================== 11 | 12 | use super::{k_tan, rem_pio2}; 13 | 14 | // tan(x) 15 | // Return tangent function of x. 16 | // 17 | // kernel function: 18 | // k_tan ... tangent function on [-pi/4,pi/4] 19 | // rem_pio2 ... argument reduction routine 20 | // 21 | // Method. 22 | // Let S,C and T denote the sin, cos and tan respectively on 23 | // [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 24 | // in [-pi/4 , +pi/4], and let n = k mod 4. 25 | // We have 26 | // 27 | // n sin(x) cos(x) tan(x) 28 | // ---------------------------------------------------------- 29 | // 0 S C T 30 | // 1 C -S -1/T 31 | // 2 -S -C T 32 | // 3 -C S -1/T 33 | // ---------------------------------------------------------- 34 | // 35 | // Special cases: 36 | // Let trig be any of sin, cos, or tan. 37 | // trig(+-INF) is NaN, with signals; 38 | // trig(NaN) is that NaN; 39 | // 40 | // Accuracy: 41 | // TRIG(x) returns trig(x) nearly rounded 42 | 43 | /// The tangent of `x` (f64). 44 | /// 45 | /// `x` is specified in radians. 46 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 47 | pub fn tan(x: f64) -> f64 { 48 | let x1p120 = f32::from_bits(0x7b800000); // 0x1p120f === 2 ^ 120 49 | 50 | let ix = (f64::to_bits(x) >> 32) as u32 & 0x7fffffff; 51 | /* |x| ~< pi/4 */ 52 | if ix <= 0x3fe921fb { 53 | if ix < 0x3e400000 { 54 | /* |x| < 2**-27 */ 55 | /* raise inexact if x!=0 and underflow if subnormal */ 56 | force_eval!(if ix < 0x00100000 { x / x1p120 as f64 } else { x + x1p120 as f64 }); 57 | return x; 58 | } 59 | return k_tan(x, 0.0, 0); 60 | } 61 | 62 | /* tan(Inf or NaN) is NaN */ 63 | if ix >= 0x7ff00000 { 64 | return x - x; 65 | } 66 | 67 | /* argument reduction */ 68 | let (n, y0, y1) = rem_pio2(x); 69 | k_tan(y0, y1, n & 1) 70 | } 71 | -------------------------------------------------------------------------------- /libm/src/math/tanf.rs: -------------------------------------------------------------------------------- 1 | /* origin: FreeBSD /usr/src/lib/msun/src/s_tanf.c */ 2 | /* 3 | * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. 4 | * Optimized by Bruce D. Evans. 5 | */ 6 | /* 7 | * ==================================================== 8 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 9 | * 10 | * Developed at SunPro, a Sun Microsystems, Inc. business. 11 | * Permission to use, copy, modify, and distribute this 12 | * software is freely granted, provided that this notice 13 | * is preserved. 14 | * ==================================================== 15 | */ 16 | 17 | use core::f64::consts::FRAC_PI_2; 18 | 19 | use super::{k_tanf, rem_pio2f}; 20 | 21 | /* Small multiples of pi/2 rounded to double precision. */ 22 | const T1_PIO2: f64 = 1. * FRAC_PI_2; /* 0x3FF921FB, 0x54442D18 */ 23 | const T2_PIO2: f64 = 2. * FRAC_PI_2; /* 0x400921FB, 0x54442D18 */ 24 | const T3_PIO2: f64 = 3. * FRAC_PI_2; /* 0x4012D97C, 0x7F3321D2 */ 25 | const T4_PIO2: f64 = 4. * FRAC_PI_2; /* 0x401921FB, 0x54442D18 */ 26 | 27 | /// The tangent of `x` (f32). 28 | /// 29 | /// `x` is specified in radians. 30 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 31 | pub fn tanf(x: f32) -> f32 { 32 | let x64 = x as f64; 33 | 34 | let x1p120 = f32::from_bits(0x7b800000); // 0x1p120f === 2 ^ 120 35 | 36 | let mut ix = x.to_bits(); 37 | let sign = (ix >> 31) != 0; 38 | ix &= 0x7fffffff; 39 | 40 | if ix <= 0x3f490fda { 41 | /* |x| ~<= pi/4 */ 42 | if ix < 0x39800000 { 43 | /* |x| < 2**-12 */ 44 | /* raise inexact if x!=0 and underflow if subnormal */ 45 | force_eval!(if ix < 0x00800000 { x / x1p120 } else { x + x1p120 }); 46 | return x; 47 | } 48 | return k_tanf(x64, false); 49 | } 50 | if ix <= 0x407b53d1 { 51 | /* |x| ~<= 5*pi/4 */ 52 | if ix <= 0x4016cbe3 { 53 | /* |x| ~<= 3pi/4 */ 54 | return k_tanf(if sign { x64 + T1_PIO2 } else { x64 - T1_PIO2 }, true); 55 | } else { 56 | return k_tanf(if sign { x64 + T2_PIO2 } else { x64 - T2_PIO2 }, false); 57 | } 58 | } 59 | if ix <= 0x40e231d5 { 60 | /* |x| ~<= 9*pi/4 */ 61 | if ix <= 0x40afeddf { 62 | /* |x| ~<= 7*pi/4 */ 63 | return k_tanf(if sign { x64 + T3_PIO2 } else { x64 - T3_PIO2 }, true); 64 | } else { 65 | return k_tanf(if sign { x64 + T4_PIO2 } else { x64 - T4_PIO2 }, false); 66 | } 67 | } 68 | 69 | /* tan(Inf or NaN) is NaN */ 70 | if ix >= 0x7f800000 { 71 | return x - x; 72 | } 73 | 74 | /* argument reduction */ 75 | let (n, y) = rem_pio2f(x); 76 | k_tanf(y, n & 1 != 0) 77 | } 78 | -------------------------------------------------------------------------------- /libm/src/math/tanh.rs: -------------------------------------------------------------------------------- 1 | use super::expm1; 2 | 3 | /* tanh(x) = (exp(x) - exp(-x))/(exp(x) + exp(-x)) 4 | * = (exp(2*x) - 1)/(exp(2*x) - 1 + 2) 5 | * = (1 - exp(-2*x))/(exp(-2*x) - 1 + 2) 6 | */ 7 | 8 | /// The hyperbolic tangent of `x` (f64). 9 | /// 10 | /// `x` is specified in radians. 11 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 12 | pub fn tanh(mut x: f64) -> f64 { 13 | let mut uf: f64 = x; 14 | let mut ui: u64 = f64::to_bits(uf); 15 | 16 | let w: u32; 17 | let sign: bool; 18 | let mut t: f64; 19 | 20 | /* x = |x| */ 21 | sign = ui >> 63 != 0; 22 | ui &= !1 / 2; 23 | uf = f64::from_bits(ui); 24 | x = uf; 25 | w = (ui >> 32) as u32; 26 | 27 | if w > 0x3fe193ea { 28 | /* |x| > log(3)/2 ~= 0.5493 or nan */ 29 | if w > 0x40340000 { 30 | /* |x| > 20 or nan */ 31 | /* note: this branch avoids raising overflow */ 32 | t = 1.0 - 0.0 / x; 33 | } else { 34 | t = expm1(2.0 * x); 35 | t = 1.0 - 2.0 / (t + 2.0); 36 | } 37 | } else if w > 0x3fd058ae { 38 | /* |x| > log(5/3)/2 ~= 0.2554 */ 39 | t = expm1(2.0 * x); 40 | t = t / (t + 2.0); 41 | } else if w >= 0x00100000 { 42 | /* |x| >= 0x1p-1022, up to 2ulp error in [0.1,0.2554] */ 43 | t = expm1(-2.0 * x); 44 | t = -t / (t + 2.0); 45 | } else { 46 | /* |x| is subnormal */ 47 | /* note: the branch above would not raise underflow in [0x1p-1023,0x1p-1022) */ 48 | force_eval!(x as f32); 49 | t = x; 50 | } 51 | 52 | if sign { -t } else { t } 53 | } 54 | -------------------------------------------------------------------------------- /libm/src/math/tanhf.rs: -------------------------------------------------------------------------------- 1 | use super::expm1f; 2 | 3 | /// The hyperbolic tangent of `x` (f32). 4 | /// 5 | /// `x` is specified in radians. 6 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 7 | pub fn tanhf(mut x: f32) -> f32 { 8 | /* x = |x| */ 9 | let mut ix = x.to_bits(); 10 | let sign = (ix >> 31) != 0; 11 | ix &= 0x7fffffff; 12 | x = f32::from_bits(ix); 13 | let w = ix; 14 | 15 | let tt = if w > 0x3f0c9f54 { 16 | /* |x| > log(3)/2 ~= 0.5493 or nan */ 17 | if w > 0x41200000 { 18 | /* |x| > 10 */ 19 | 1. + 0. / x 20 | } else { 21 | let t = expm1f(2. * x); 22 | 1. - 2. / (t + 2.) 23 | } 24 | } else if w > 0x3e82c578 { 25 | /* |x| > log(5/3)/2 ~= 0.2554 */ 26 | let t = expm1f(2. * x); 27 | t / (t + 2.) 28 | } else if w >= 0x00800000 { 29 | /* |x| >= 0x1p-126 */ 30 | let t = expm1f(-2. * x); 31 | -t / (t + 2.) 32 | } else { 33 | /* |x| is subnormal */ 34 | force_eval!(x * x); 35 | x 36 | }; 37 | if sign { -tt } else { tt } 38 | } 39 | -------------------------------------------------------------------------------- /libm/src/math/tgammaf.rs: -------------------------------------------------------------------------------- 1 | use super::tgamma; 2 | 3 | /// The [Gamma function](https://en.wikipedia.org/wiki/Gamma_function) (f32). 4 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 5 | pub fn tgammaf(x: f32) -> f32 { 6 | tgamma(x as f64) as f32 7 | } 8 | -------------------------------------------------------------------------------- /libm/src/math/trunc.rs: -------------------------------------------------------------------------------- 1 | /// Rounds the number toward 0 to the closest integral value (f16). 2 | /// 3 | /// This effectively removes the decimal part of the number, leaving the integral part. 4 | #[cfg(f16_enabled)] 5 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 6 | pub fn truncf16(x: f16) -> f16 { 7 | super::generic::trunc(x) 8 | } 9 | 10 | /// Rounds the number toward 0 to the closest integral value (f32). 11 | /// 12 | /// This effectively removes the decimal part of the number, leaving the integral part. 13 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 14 | pub fn truncf(x: f32) -> f32 { 15 | select_implementation! { 16 | name: truncf, 17 | use_arch: all(target_arch = "wasm32", intrinsics_enabled), 18 | args: x, 19 | } 20 | 21 | super::generic::trunc(x) 22 | } 23 | 24 | /// Rounds the number toward 0 to the closest integral value (f64). 25 | /// 26 | /// This effectively removes the decimal part of the number, leaving the integral part. 27 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 28 | pub fn trunc(x: f64) -> f64 { 29 | select_implementation! { 30 | name: trunc, 31 | use_arch: all(target_arch = "wasm32", intrinsics_enabled), 32 | args: x, 33 | } 34 | 35 | super::generic::trunc(x) 36 | } 37 | 38 | /// Rounds the number toward 0 to the closest integral value (f128). 39 | /// 40 | /// This effectively removes the decimal part of the number, leaving the integral part. 41 | #[cfg(f128_enabled)] 42 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 43 | pub fn truncf128(x: f128) -> f128 { 44 | super::generic::trunc(x) 45 | } 46 | 47 | #[cfg(test)] 48 | mod tests { 49 | #[test] 50 | fn sanity_check() { 51 | assert_eq!(super::truncf(1.1), 1.0); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /libm/src/math/truncf.rs: -------------------------------------------------------------------------------- 1 | /// Rounds the number toward 0 to the closest integral value (f32). 2 | /// 3 | /// This effectively removes the decimal part of the number, leaving the integral part. 4 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 5 | pub fn truncf(x: f32) -> f32 { 6 | select_implementation! { 7 | name: truncf, 8 | use_arch: all(target_arch = "wasm32", intrinsics_enabled), 9 | args: x, 10 | } 11 | 12 | super::generic::trunc(x) 13 | } 14 | 15 | // PowerPC tests are failing on LLVM 13: https://github.com/rust-lang/rust/issues/88520 16 | #[cfg(not(target_arch = "powerpc64"))] 17 | #[cfg(test)] 18 | mod tests { 19 | #[test] 20 | fn sanity_check() { 21 | assert_eq!(super::truncf(1.1), 1.0); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /libm/src/math/truncf128.rs: -------------------------------------------------------------------------------- 1 | /// Rounds the number toward 0 to the closest integral value (f128). 2 | /// 3 | /// This effectively removes the decimal part of the number, leaving the integral part. 4 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 5 | pub fn truncf128(x: f128) -> f128 { 6 | super::generic::trunc(x) 7 | } 8 | -------------------------------------------------------------------------------- /libm/src/math/truncf16.rs: -------------------------------------------------------------------------------- 1 | /// Rounds the number toward 0 to the closest integral value (f16). 2 | /// 3 | /// This effectively removes the decimal part of the number, leaving the integral part. 4 | #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] 5 | pub fn truncf16(x: f16) -> f16 { 6 | super::generic::trunc(x) 7 | } 8 | --------------------------------------------------------------------------------