├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE.md ├── README.md ├── appveyor.yml ├── ci ├── install.ps1 ├── package.ps1 ├── package.sh └── script.sh └── src ├── lib.rs ├── ll.rs ├── m.rs └── qc.rs /.gitignore: -------------------------------------------------------------------------------- 1 | *.rs.bk 2 | Cargo.lock 3 | target 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | language: rust 3 | rust: stable 4 | services: docker 5 | sudo: required 6 | 7 | matrix: 8 | include: 9 | # OSX 10 | - env: TARGET=i686-apple-darwin 11 | os: osx 12 | - env: TARGET=x86_64-apple-darwin 13 | os: osx 14 | 15 | # Linux 16 | - env: TARGET=aarch64-unknown-linux-gnu 17 | - env: TARGET=armv7-unknown-linux-gnueabihf 18 | - env: TARGET=i686-unknown-linux-gnu 19 | - env: TARGET=mips-unknown-linux-gnu 20 | - env: TARGET=mips64-unknown-linux-gnuabi64 21 | - env: TARGET=mips64el-unknown-linux-gnuabi64 22 | - env: TARGET=mipsel-unknown-linux-gnu 23 | - env: TARGET=powerpc-unknown-linux-gnu 24 | - env: TARGET=powerpc64-unknown-linux-gnu 25 | - env: TARGET=powerpc64le-unknown-linux-gnu 26 | - env: TARGET=s390x-unknown-linux-gnu 27 | env: TARGET=x86_64-unknown-linux-gnu 28 | 29 | install: 30 | - curl https://sh.rustup.rs -sSf | 31 | sh -s -- -y --default-toolchain $TRAVIS_RUST_VERSION; 32 | - source ~/.cargo/env 33 | - cargo install cross -f 34 | 35 | script: 36 | - sh ci/script.sh 37 | 38 | cache: cargo 39 | before_cache: 40 | # Travis can't cache files that are not readable by "others" 41 | - chmod -R a+r $HOME/.cargo 42 | 43 | branches: 44 | only: 45 | - auto 46 | - try 47 | 48 | notifications: 49 | email: 50 | on_success: never 51 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## [Unreleased] 9 | 10 | ## [v0.1.1] - 2016-10-19 11 | 12 | ### Added 13 | 14 | - generic `sqrtf` 15 | 16 | ## v0.1.0 - 2016-10-15 17 | 18 | ### Added 19 | 20 | - generic `atan2f`, `atanf` `fabs`, `fasbf` 21 | 22 | [Unreleased]: https://github.com/japaric/m/compare/v0.1.1...HEAD 23 | [v0.1.1]: https://github.com/japaric/m/compare/v0.1.0...v0.1.1 24 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Jorge Aparicio "] 3 | description = "A C free / pure Rust mathematical library (\"libm\") for `no_std` code" 4 | documentation = "https://docs.rs/m" 5 | keywords = ["float", "math", "libm", "no_std"] 6 | license = "BSD-2-Clause AND ISC AND MIT" 7 | name = "m" 8 | repository = "https://github.com/japaric/m" 9 | version = "0.1.1" 10 | 11 | [dev-dependencies] 12 | quickcheck = "0.3.1" 13 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ============================================================================== 2 | m License 3 | ============================================================================== 4 | 5 | The m crate is a port of the OpenLibm library, which contains code that is 6 | covered by various licenses: 7 | 8 | The OpenLibm code derives from the FreeBSD msun and OpenBSD libm 9 | implementations, which in turn derives from FDLIBM 5.3. As a result, it has a 10 | number of fixes and updates that have accumulated over the years in msun, and 11 | also optimized assembly versions of many functions. These improvements are 12 | provided under the BSD and ISC licenses. The msun library also includes work 13 | placed under the public domain, which is noted in the individual files. Further 14 | work on making a standalone OpenLibm library from msun, as part of the Julia 15 | project is covered under the MIT license. 16 | 17 | ## Parts copyrighted by the Julia project (MIT License) 18 | 19 | > Copyright (c) 2011-14 The Julia Project. 20 | > https://github.com/JuliaLang/openlibm/graphs/contributors 21 | > 22 | > Permission is hereby granted, free of charge, to any person obtaining 23 | > a copy of this software and associated documentation files (the 24 | > "Software"), to deal in the Software without restriction, including 25 | > without limitation the rights to use, copy, modify, merge, publish, 26 | > distribute, sublicense, and/or sell copies of the Software, and to 27 | > permit persons to whom the Software is furnished to do so, subject to 28 | > the following conditions: 29 | > 30 | > The above copyright notice and this permission notice shall be 31 | > included in all copies or substantial portions of the Software. 32 | > 33 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 34 | > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 35 | > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 36 | > NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 37 | > LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 38 | > OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 39 | > WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 40 | 41 | ## Parts copyrighted by Stephen L. Moshier (ISC License) 42 | 43 | > Copyright (c) 2008 Stephen L. Moshier 44 | > 45 | > Permission to use, copy, modify, and distribute this software for any 46 | > purpose with or without fee is hereby granted, provided that the above 47 | > copyright notice and this permission notice appear in all copies. 48 | > 49 | > THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 50 | > WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 51 | > MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 52 | > ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 53 | > WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 54 | > ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 55 | > OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 56 | 57 | ## FREEBSD MSUN (FreeBSD/2-clause BSD/Simplified BSD License) 58 | 59 | > Copyright 1992-2011 The FreeBSD Project. All rights reserved. 60 | > 61 | > Redistribution and use in source and binary forms, with or without 62 | > modification, are permitted provided that the following conditions are 63 | > met: 64 | > 65 | > 1. Redistributions of source code must retain the above copyright 66 | > notice, this list of conditions and the following disclaimer. 67 | > 68 | > 2. Redistributions in binary form must reproduce the above copyright 69 | > notice, this list of conditions and the following disclaimer in the 70 | > documentation and/or other materials provided with the distribution. 71 | > THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY 72 | > EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 73 | > IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 74 | > PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR 75 | > CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 76 | > EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 77 | > PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 78 | > PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 79 | > LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 80 | > NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 81 | > SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 82 | > 83 | > The views and conclusions contained in the software and documentation 84 | > are those of the authors and should not be interpreted as representing 85 | > official policies, either expressed or implied, of the FreeBSD 86 | > Project. 87 | 88 | ## FDLIBM 89 | 90 | > Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. 91 | > 92 | > Developed at SunPro, a Sun Microsystems, Inc. business. 93 | > Permission to use, copy, modify, and distribute this 94 | > software is freely granted, provided that this notice 95 | > is preserved. 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PSA: this crate has been deprecated in favor of [`libm`] 2 | 3 | [`libm`]: https://github.com/japaric/libm 4 | 5 | `libm` is a port of MUSL's libm. MUSL source code proved much easier to translate to Rust, and more 6 | importantly it was easier to write a test generator that didn't require compiling C code and that 7 | worked on a bunch of different architectures. 8 | 9 | These two aspects make such a difference that we were able to [port 30 math functions to Rust in less 10 | than 24 hours][libm-changelog]. 11 | 12 | [libm-changelog]: https://github.com/japaric/libm/blob/master/CHANGELOG.md#v011---2018-07-14 13 | 14 | -@japaric, 2018-07-14 15 | 16 | --- 17 | 18 | [![crates.io](https://img.shields.io/crates/d/m.svg)](https://crates.io/crates/m) 19 | [![crates.io](https://img.shields.io/crates/v/m.svg)](https://crates.io/crates/m) 20 | 21 | # `m` 22 | 23 | > A C free / pure Rust mathematical library ("libm") for `no_std` code 24 | 25 | This is a port of [OpenLibm]. 26 | 27 | [openlibm]: https://github.com/JuliaLang/openlibm 28 | 29 | ## [Documentation](https://docs.rs/m) 30 | 31 | ## [Change log](CHANGELOG.md) 32 | 33 | ## License 34 | 35 | The m crate is a port of the OpenLibm library, which contains code that is 36 | covered by various licenses: 37 | 38 | The OpenLibm code derives from the FreeBSD msun and OpenBSD libm 39 | implementations, which in turn derives from FDLIBM 5.3. As a result, it has a 40 | number of fixes and updates that have accumulated over the years in msun, and 41 | also optimized assembly versions of many functions. These improvements are 42 | provided under the BSD and ISC licenses. The msun library also includes work 43 | placed under the public domain, which is noted in the individual files. Further 44 | work on making a standalone OpenLibm library from msun, as part of the Julia 45 | project is covered under the MIT license. 46 | 47 | TL;DR OpenLibm contains code that is licensed under the 2-clause BSD, the ISC 48 | and the MIT licenses and code that is in the public domain. As a user of this 49 | code you agree to use it under these licenses. As a contributor, you agree to 50 | allow your code to be used under all these licenses as well. 51 | 52 | Full text of the relevant licenses is in LICENSE.md. 53 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | global: 3 | RUST_VERSION: stable 4 | matrix: 5 | # NOTE(msvc) linker error "unresolved external symbol _atan2f" 6 | # - TARGET: i586-pc-windows-msvc 7 | - TARGET: i686-pc-windows-gnu 8 | # - TARGET: i686-pc-windows-msvc 9 | - TARGET: x86_64-pc-windows-gnu 10 | - TARGET: x86_64-pc-windows-msvc 11 | 12 | install: 13 | - ps: ci\install.ps1 14 | 15 | build: false 16 | 17 | test_script: 18 | - cargo build --target %TARGET% 19 | - cargo build --target %TARGET% --release 20 | - cargo test --target %TARGET% 21 | - cargo test --target %TARGET% --release 22 | 23 | cache: 24 | - C:\Users\appveyor\.cargo\registry 25 | - target 26 | 27 | branches: 28 | only: 29 | - auto 30 | - try 31 | 32 | notifications: 33 | - provider: Email 34 | on_build_success: false 35 | -------------------------------------------------------------------------------- /ci/install.ps1: -------------------------------------------------------------------------------- 1 | $Env:HOST = $Env:TARGET 2 | 3 | If ($Env:TARGET -Match 'gnu') { 4 | if ($Env:TARGET -Match 'x86_64') { 5 | $Env:PATH += ';C:\msys64\mingw64\bin' 6 | } else { 7 | $Env:PATH += ';C:\msys64\mingw32\bin' 8 | } 9 | } ElseIf ($Env:TARGET -Match 'i586') { 10 | $Env:HOST = 'i686-pc-windows-msvc' 11 | } 12 | 13 | [Net.ServicePointManager]::SecurityProtocol = 'Ssl3, Tls, Tls12' 14 | Start-FileDownload 'https://win.rustup.rs' 'rustup-init.exe' 15 | 16 | .\rustup-init --default-host $Env:HOST --default-toolchain $Env:RUST_VERSION -y 17 | 18 | $Env:PATH = 'C:\Users\appveyor\.cargo\bin;' + $Env:PATH 19 | 20 | If ($Env:TARGET -ne $Env:HOST) { 21 | rustup target add $Env:TARGET 22 | } 23 | 24 | rustc -Vv 25 | 26 | cargo -V 27 | -------------------------------------------------------------------------------- /ci/package.ps1: -------------------------------------------------------------------------------- 1 | $SRC_DIR = $PWD.Path 2 | $STAGE = [System.Guid]::NewGuid().ToString() 3 | 4 | Set-Location $ENV:Temp 5 | New-Item -Type Directory -Name $STAGE 6 | Set-Location $STAGE 7 | 8 | $ZIP = "$SRC_DIR\$($Env:CRATE_NAME)-$($Env:APPVEYOR_REPO_TAG_NAME)-$($Env:TARGET).zip" 9 | Copy-Item "$SRC_DIR\target\$($Env:TARGET)\release\hello.exe" '.\' 10 | 7z a "$ZIP" * 11 | 12 | Push-AppveyorArtifact "$ZIP" 13 | 14 | Remove-Item *.* -Force 15 | Set-Location .. 16 | Remove-Item $STAGE 17 | Set-Location $SRC_DIR 18 | -------------------------------------------------------------------------------- /ci/package.sh: -------------------------------------------------------------------------------- 1 | set -ex 2 | 3 | run() { 4 | local src_dir=$(pwd)\ 5 | stage=$(mk_temp_dir) 6 | 7 | cp target/$TARGET/release/hello $stage/ 8 | 9 | cd $stage 10 | tar czf $src_dir/$CRATE_NAME-$TRAVIS_TAG-$TARGET.tar.gz * 11 | cd $src_dir 12 | 13 | rm -rf $stage 14 | } 15 | 16 | mk_temp_dir() { 17 | case $TRAVIS_OS_NAME in 18 | linux) 19 | mktemp -d 20 | ;; 21 | osx) 22 | mktemp -d -t tmp 23 | ;; 24 | esac 25 | } 26 | 27 | run 28 | -------------------------------------------------------------------------------- /ci/script.sh: -------------------------------------------------------------------------------- 1 | set -ex 2 | 3 | main() { 4 | cross generate-lockfile 5 | 6 | cross build --target $TARGET 7 | cross build --target $TARGET --release 8 | 9 | if [ $TARGET != s390x-unknown-linux-gnu ]; then 10 | cross test --target $TARGET 11 | cross test --target $TARGET --release 12 | fi 13 | } 14 | 15 | main 16 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A C free / pure Rust mathematical library ("libm") for `no_std` code 2 | //! 3 | //! This is a port of [`OpenLibm`]. 4 | //! 5 | //! [`OpenLibm`]: https://github.com/JuliaLang/openlibm 6 | //! 7 | //! # Usage 8 | //! 9 | //! Currently, this crate only provides a `Float` extension trait that's very 10 | //! similar to the one in std but its method are implemented in pure Rust 11 | //! instead of its methods being wrappers over the system libm. For this reason, 12 | //! this trait is usable in C free, `no_std` environments. 13 | //! 14 | //! ``` 15 | //! #![no_std] 16 | //! 17 | //! use m::Float as _0; 18 | //! 19 | //! fn foo(x: f32) -> f32 { 20 | //! x.atan() 21 | //! } 22 | //! ``` 23 | //! 24 | //! Mind you that, at the moment, this extension trait only provides a handful 25 | //! of mathematical functions -- only the ones that have been ported to Rust. 26 | //! 27 | //! # Coverage 28 | //! 29 | //! So far, these functions have been ported to Rust: 30 | //! 31 | //! - `atan2f` 32 | //! - `atanf` 33 | //! - `fabs` 34 | //! - `fabsf` 35 | 36 | #![allow(unknown_lints)] 37 | #![cfg_attr(not(test), no_std)] 38 | #![deny(warnings)] 39 | 40 | #[cfg(test)] 41 | extern crate core; 42 | 43 | #[cfg(test)] 44 | #[macro_use] 45 | extern crate quickcheck; 46 | 47 | use core::mem; 48 | use core::{f32, f64}; 49 | 50 | #[cfg(test)] 51 | mod m; 52 | 53 | #[cfg(test)] 54 | #[macro_use] 55 | mod qc; 56 | 57 | mod ll; 58 | 59 | /// Trait that provides mathematical functions for floating point numbers 60 | /// 61 | /// # Fine print 62 | /// 63 | /// This trait is meant to be a "closed" extension trait that's not meant to be 64 | /// implemented by downstream users. As such this trait is *exempted* from 65 | /// semver rules in the context of *adding* new methods to it. Therefore: if you 66 | /// implement this trait (don't do that), your code may (will!) break during a 67 | /// minor version bump. You have been warned! 68 | pub trait Float { 69 | /// Computes the absolute value of `self`. Returns `NAN` if the number is 70 | /// `NAN`. 71 | fn abs(self) -> Self; 72 | 73 | /// Computes the arctangent of a number. Return value is in radians in the 74 | /// range `[-pi/2, pi/2]` 75 | fn atan(self) -> Self; 76 | 77 | /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) 78 | /// 79 | /// - `x = 0`, `y = 0`: `0` 80 | /// - `x >= 0`: `arctan(y / x)` -> `[-pi/2, pi/2]` 81 | /// - `y >= 0`: `arctan(y / x) + pi` -> `(pi/2, pi]` 82 | /// - `y < 0`: `arctan(y / x) - pi` -> `(-pi, -pi/2)` 83 | fn atan2(self, Self) -> Self; 84 | 85 | /// Returns `true` if this value is positive infinity or negative infinity 86 | /// and `false` otherwise. 87 | fn is_infinite(self) -> bool; 88 | 89 | /// Returns `true` if this value is `NaN` and `false` otherwise. 90 | fn is_nan(self) -> bool; 91 | 92 | /// Takes the square root of a number 93 | /// 94 | /// Returns `NaN` if `self` is a negative number 95 | fn sqrt(self) -> Self; 96 | } 97 | 98 | macro_rules! float { 99 | ($ty:ident, 100 | atan = $atan:ident, 101 | atan2 = $atan2:ident, 102 | fabs = $fabs:ident, 103 | sqrt = $sqrt:ident) => { 104 | impl Float for $ty { 105 | fn abs(self) -> Self { 106 | ll::$fabs(self) 107 | } 108 | 109 | fn atan(self) -> Self { 110 | ll::$atan(self) 111 | } 112 | 113 | fn atan2(self, other: Self) -> Self { 114 | ll::$atan2(self, other) 115 | } 116 | 117 | fn is_infinite(self) -> bool { 118 | self == $ty::INFINITY || self == $ty::NEG_INFINITY 119 | } 120 | 121 | fn is_nan(self) -> bool { 122 | #![allow(eq_op)] 123 | 124 | self != self 125 | } 126 | 127 | fn sqrt(self) -> Self { 128 | ll::$sqrt(self) 129 | } 130 | } 131 | 132 | } 133 | } 134 | 135 | float!(f32, 136 | atan = atanf, 137 | atan2 = atan2f, 138 | fabs = fabsf, 139 | sqrt = sqrtf); 140 | float!(f64, atan = atan, atan2 = atan2, fabs = fabs, sqrt = sqrt); 141 | 142 | trait FloatExt { 143 | type Int; 144 | 145 | fn bits() -> u32; 146 | #[cfg(test)] 147 | fn eq_repr(self, Self) -> bool; 148 | fn exponent(self) -> i16; 149 | fn exponent_bias() -> u32; 150 | fn exponent_bits() -> u32; 151 | fn exponent_mask() -> Self::Int; 152 | fn from_parts(sign: Sign, 153 | exponent: Self::Int, 154 | significand: Self::Int) 155 | -> Self; 156 | fn from_repr(Self::Int) -> Self; 157 | fn repr(self) -> Self::Int; 158 | fn sign(self) -> Sign; 159 | fn sign_mask() -> Self::Int; 160 | fn significand_bits() -> u32; 161 | fn significand_mask() -> Self::Int; 162 | } 163 | 164 | macro_rules! float_ext { 165 | ($float_ty:ident, 166 | repr_ty = $repr_ty:ident, 167 | exponent_bits = $exponent_bits:expr, 168 | significand_bits = $significand_bits:expr) => { 169 | impl FloatExt for $float_ty { 170 | type Int = $repr_ty; 171 | 172 | fn bits() -> u32 { 173 | 1 + Self::exponent_bits() + Self::significand_bits() 174 | } 175 | 176 | #[cfg(test)] 177 | fn eq_repr(self, rhs: Self) -> bool { 178 | if self.is_nan() && rhs.is_nan() { 179 | true 180 | } else { 181 | let (lhs, rhs) = (self.repr(), rhs.repr()); 182 | 183 | lhs == rhs || (lhs > rhs && lhs - rhs == 1) || 184 | (rhs > lhs && rhs - lhs == 1) 185 | } 186 | } 187 | 188 | fn exponent(self) -> i16 { 189 | ((self.repr() & Self::exponent_mask()) >> 190 | Self::significand_bits()) as i16 - 191 | Self::exponent_bias() as i16 192 | } 193 | 194 | fn exponent_bias() -> u32 { 195 | (1 << (Self::exponent_bits() - 1)) - 1 196 | } 197 | 198 | fn exponent_bits() -> u32 { 199 | $exponent_bits 200 | } 201 | 202 | fn exponent_mask() -> Self::Int { 203 | ((1 << Self::exponent_bits()) - 1) << Self::significand_bits() 204 | } 205 | 206 | fn from_parts(sign: Sign, 207 | exponent: Self::Int, 208 | significand: Self::Int) -> Self { 209 | Self::from_repr(sign.$repr_ty() | 210 | exponent & Self::exponent_mask() | 211 | significand & Self::significand_mask()) 212 | } 213 | 214 | fn from_repr(x: Self::Int) -> Self { 215 | unsafe { mem::transmute(x) } 216 | } 217 | 218 | fn repr(self) -> Self::Int { 219 | unsafe { mem::transmute(self) } 220 | } 221 | 222 | fn sign(self) -> Sign { 223 | if self.repr() >> (Self::bits() - 1) == 0 { 224 | Sign::Positive 225 | } else { 226 | Sign::Negative 227 | } 228 | } 229 | 230 | fn sign_mask() -> Self::Int { 231 | (1 << (Self::bits() - 1)) - 1 232 | } 233 | 234 | fn significand_bits() -> u32 { 235 | $significand_bits 236 | } 237 | 238 | fn significand_mask() -> Self::Int { 239 | (1 << Self::significand_bits()) - 1 240 | } 241 | } 242 | } 243 | } 244 | 245 | float_ext!(f32, repr_ty = u32, exponent_bits = 8, significand_bits = 23); 246 | float_ext!(f64, 247 | repr_ty = u64, 248 | exponent_bits = 11, 249 | significand_bits = 52); 250 | 251 | #[derive(Eq, PartialEq)] 252 | enum Sign { 253 | Negative, 254 | Positive, 255 | } 256 | 257 | impl Sign { 258 | #[cfg(test)] 259 | fn from_bool(x: bool) -> Self { 260 | if x { Sign::Negative } else { Sign::Positive } 261 | } 262 | 263 | fn u32(self) -> u32 { 264 | match self { 265 | Sign::Positive => 0, 266 | Sign::Negative => 1 << 31, 267 | } 268 | } 269 | 270 | fn u64(self) -> u64 { 271 | match self { 272 | Sign::Positive => 0, 273 | Sign::Negative => 1 << 63, 274 | } 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /src/ll.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | 3 | #[cfg(not(test))] 4 | use Float; 5 | use {FloatExt, Sign}; 6 | 7 | pub extern "C" fn atan(_: f64) -> f64 { 8 | unimplemented!() 9 | } 10 | 11 | // # Method 12 | // 13 | // 1. Reduce x to positive by atan(x) = -atan(-x) 14 | // 2. According to the integer k = 4*t + 0.25 chopped, t=x, the argument 15 | // is further reduced to one of the following intervals and the 16 | // arctangent of t is evaluated by the corresponding formula: 17 | // 18 | // [0, 7/16] atan(x) = t - t^3 + (a1 + t^2 * (a2 + ... (a10 + a11 * t^2)...)) 19 | // [7/16, 11/16] atan(x) = atan(1/2) + atan((t - 0.5) / (1 + t/2) 20 | // [11/16, 19/16] atan(x) = atan(1) + atan((t - 1) / (1 + t)) 21 | // [19/16, 39/16] atan(x) = atan(3/2) + atan((t - 1.5) / (1 + 1.5*t)) 22 | // [39/16, INF] atan(x) = atan(INF) + atan(-1/t) 23 | pub extern "C" fn atanf(mut x: f32) -> f32 { 24 | const A: [f32; 5] = [3.3333328366e-01, 25 | -1.9999158382e-01, 26 | 1.4253635705e-01, 27 | -1.0648017377e-01, 28 | 6.1687607318e-02]; 29 | const HUGE: f32 = 1e30; 30 | 31 | // `[atan(0.5), atan(1.0), atan(1.5), atan(inf)]` 32 | let atanhi: [f32; 4] = unsafe { 33 | [mem::transmute(0x3eed6338), 34 | mem::transmute(0x3f490fda), 35 | mem::transmute(0x3f7b985e), 36 | mem::transmute(0x3fc90fda)] 37 | }; 38 | 39 | // `[atan(0.5), atan(1.0), atan(1.5), atan(inf)]` 40 | let atanlo: [f32; 4] = unsafe { 41 | [mem::transmute(0x31ac3769), 42 | mem::transmute(0x33222168), 43 | mem::transmute(0x33140fb4), 44 | mem::transmute(0x33a22168)] 45 | }; 46 | 47 | let sx = x.sign(); 48 | let ix = x.abs().repr() as i32; 49 | 50 | if ix >= 0x4c800000 { 51 | if ix > 0x7f800000 { 52 | // NaN 53 | return x + x; 54 | } 55 | 56 | if sx == Sign::Positive { 57 | atanhi[3] + atanlo[3] 58 | } else { 59 | -atanhi[3] - atanlo[3] 60 | } 61 | } else { 62 | let id: i32; 63 | 64 | // |x| < 7/16 65 | if ix < 0x3ee00000 { 66 | // |x| < 2**-12 67 | if ix < 0x39800000 && HUGE + x > 1. { 68 | return x; 69 | } 70 | 71 | id = -1; 72 | } else { 73 | x = fabsf(x); 74 | 75 | // |x| < 19/16 76 | if ix < 0x3f980000 { 77 | // 7/16 <= |x| < 11/16 78 | if ix < 0x3f300000 { 79 | id = 0; 80 | x = (2. * x - 1.) / (2. + x); 81 | } else { 82 | id = 1; 83 | x = (x - 1.) / (x + 1.); 84 | } 85 | } else if ix < 0x401c0000 { 86 | // |x| < 39/16 87 | id = 2; 88 | x = (x - 1.5) / (1. + 1.5 * x); 89 | } else { 90 | // 39/16 <= |x| < 2**26 91 | id = 3; 92 | x = -1. / x; 93 | } 94 | 95 | } 96 | 97 | let z = x * x; 98 | let w = z * z; 99 | 100 | let s1 = z * (A[0] + w * (A[2] + w * A[4])); 101 | let s2 = w * (A[1] + w * A[3]); 102 | 103 | if id < 0 { 104 | x - x * (s1 + s2) 105 | } else { 106 | let id = id as usize; 107 | let z = atanhi[id] - ((x * (s1 + s2) - atanlo[id]) - x); 108 | 109 | if let Sign::Negative = sx { -z } else { z } 110 | } 111 | } 112 | } 113 | 114 | macro_rules! fabs { 115 | ($fname: ident, $ty:ident) => { 116 | pub extern fn $fname(x: $ty) -> $ty { 117 | $ty::from_repr(x.repr() & $ty::sign_mask()) 118 | } 119 | } 120 | } 121 | 122 | // atan2!(atan2, f64, 26, atan = atan, fabs = fabs); 123 | // atan2!(atan2f, f32, 60, atan = atanf, fabs = fabsf); 124 | fabs!(fabs, f64); 125 | fabs!(fabsf, f32); 126 | 127 | pub extern "C" fn atan2(_: f64, _: f64) -> f64 { 128 | unimplemented!() 129 | } 130 | 131 | pub extern "C" fn atan2f(y: f32, x: f32) -> f32 { 132 | #![allow(many_single_char_names)] 133 | // False positive 134 | #![allow(if_same_then_else)] 135 | 136 | use core::f32::consts::{FRAC_PI_2, FRAC_PI_4, PI}; 137 | 138 | const TINY: f32 = 1e-30; 139 | const PI_LO: f32 = -8.7422776573e-08; 140 | 141 | let hx = x.repr() as i32; 142 | let hy = y.repr() as i32; 143 | let ix = hx & 0x7fffffff; 144 | let iy = hy & 0x7fffffff; 145 | 146 | if ix > 0x7f800000 || iy > 0x7f800000 { 147 | // x or y is NaN 148 | x + y 149 | } else if hx == 0x3f800000 { 150 | // x = 1 151 | atanf(y) 152 | } else { 153 | // 2 * sign(x) + sign(y) 154 | let mut m = ((hy >> 31) & 1) | ((hx >> 30) & 2); 155 | 156 | // when y = 0 157 | if iy == 0 { 158 | match m { 159 | // atan(+-0, +..) = +-0 160 | 0 | 1 => y, 161 | // atan(+0, -..) = pi 162 | 2 => PI + TINY, 163 | // atan(-0, -..) = -pi 164 | _ => -PI - TINY, 165 | } 166 | } else if ix == 0 { 167 | // x = 0 168 | if hy < 0 { 169 | -FRAC_PI_2 - TINY 170 | } else { 171 | FRAC_PI_2 + TINY 172 | } 173 | } else if ix == 0x7f800000 { 174 | // x = INF 175 | if iy == 0x7f800000 { 176 | match m { 177 | // atan(+INF, +INF) 178 | 0 => FRAC_PI_4 + TINY, 179 | // atan(-INF, +INF) 180 | 1 => -FRAC_PI_4 - TINY, 181 | // atan(+INF, -INF) 182 | 2 => 3. * FRAC_PI_4 + TINY, 183 | // atan(-INF, -INF) 184 | _ => -3. * FRAC_PI_4 - TINY, 185 | } 186 | } else { 187 | match m { 188 | // atan(+.., +INF) 189 | 0 => 0., 190 | // atan(-.., +INF) 191 | 1 => -0., 192 | // atan(+.., -INF) 193 | 2 => PI + TINY, 194 | // atan(-.., -INF) 195 | _ => -PI - TINY, 196 | } 197 | } 198 | } else if iy == 0x7f800000 { 199 | // y = +-INF 200 | if hy < 0 { 201 | -FRAC_PI_2 - TINY 202 | } else { 203 | FRAC_PI_2 + TINY 204 | } 205 | } else { 206 | let k = (iy - ix) >> 23; 207 | 208 | // compute y/x 209 | let z = if k > 26 { 210 | // |y/x| > 2**26 211 | m &= 1; 212 | FRAC_PI_2 + 0.5 * PI_LO 213 | } else if k < -26 && hx < 0 { 214 | // 0 > |y|/x > -2**26 215 | 0. 216 | } else { 217 | // safe to do 218 | atanf(fabsf(y / x)) 219 | }; 220 | 221 | match m { 222 | // atan(+, +) 223 | 0 => z, 224 | // atan(-, +) 225 | 1 => -z, 226 | // atan(+, -) 227 | 2 => PI - (z - PI_LO), 228 | // atan(-, -) 229 | _ => (z - PI_LO) - PI, 230 | } 231 | } 232 | 233 | } 234 | } 235 | 236 | pub extern "C" fn sqrt(_: f64) -> f64 { 237 | unimplemented!() 238 | } 239 | 240 | pub extern "C" fn sqrtf(x: f32) -> f32 { 241 | #![allow(many_single_char_names)] 242 | #![allow(eq_op)] 243 | 244 | const ONE: f32 = 1.; 245 | const TINY: f32 = 1.0e-30; 246 | 247 | let mut ix = x.repr() as i32; 248 | 249 | if ix & 0x7f80_0000 == 0x7f80_0000 { 250 | x * x + x 251 | } else if ix == 0x0000_0000 || ix as u32 == 0x8000_0000 { 252 | x 253 | } else if x < 0. { 254 | (x - x) / (x - x) 255 | } else { 256 | 257 | // normalize 258 | let mut m = ix >> 23; 259 | 260 | if m == 0 { 261 | // subnormal 262 | let mut i = 0; 263 | while ix & 0x0080_0000 == 0 { 264 | ix <<= 1; 265 | i += 1; 266 | } 267 | 268 | m -= i - 1; 269 | } 270 | 271 | // unbias exponent 272 | m -= 127; 273 | ix = (ix & 0x007f_ffff) | 0x0080_0000; 274 | 275 | // oddm, double x to make it even 276 | if m & 1 != 0 { 277 | ix += ix; 278 | } 279 | 280 | // m = [m / 2] 281 | m >>= 1; 282 | 283 | // generate sqrt(x) bit by bit 284 | ix += ix; 285 | // q = sqrt(x) 286 | let mut q = 0; 287 | let mut s = 0; 288 | // r = moving bit from right to left 289 | let mut r = 0x0100_0000; 290 | 291 | let mut t; 292 | while r != 0 { 293 | t = s + r; 294 | 295 | if t <= ix { 296 | s = t + r; 297 | ix -= t; 298 | q += r; 299 | } 300 | 301 | ix += ix; 302 | r >>= 1; 303 | } 304 | 305 | // use floating add to find out rounding direction 306 | if ix != 0 { 307 | // trigger inexact flag 308 | let mut z = ONE - TINY; 309 | 310 | if z >= ONE { 311 | z = ONE + TINY; 312 | } 313 | 314 | if z > ONE { 315 | q += 2; 316 | } else { 317 | q += q & 1; 318 | } 319 | } 320 | 321 | ix = (q >> 1) + 0x3f00_0000; 322 | ix += m << 23; 323 | 324 | f32::from_repr(ix as u32) 325 | } 326 | } 327 | 328 | #[cfg(test)] 329 | check! { 330 | // `atan` has not been implemented yet 331 | // fn atan2(f: extern fn(f64, f64) -> f64, y: F64, x: F64) -> Option { 332 | // Some(F64(f(y.0, x.0))) 333 | // } 334 | 335 | fn atan2f(f: extern fn(f32, f32) -> f32, y: F32, x: F32) -> Option { 336 | Some(F32(f(y.0, x.0))) 337 | } 338 | 339 | fn atanf(f: extern fn(f32) -> f32, x: F32) -> Option { 340 | Some(F32(f(x.0))) 341 | } 342 | 343 | // unimplemented! 344 | // fn atan(f: extern fn(f64) -> f64, x: F64) -> Option { 345 | // Some(F64(f(x.0))) 346 | // } 347 | 348 | fn fabs(f: extern fn(f64) -> f64, x: F64) -> Option { 349 | Some(F64(f(x.0))) 350 | } 351 | 352 | fn fabsf(f: extern fn(f32) -> f32, x: F32) -> Option { 353 | Some(F32(f(x.0))) 354 | } 355 | 356 | // unimplemented! 357 | // fn sqrt(f: extern fn(f64) -> f64, x: F64) -> Option { 358 | // Some(F64(f(x.0))) 359 | // } 360 | 361 | fn sqrtf(f: extern fn(f32) -> f32, x: F32) -> Option { 362 | match () { 363 | #[cfg(all(target_env = "gnu", target_os = "windows"))] 364 | () => { 365 | if x.0.repr() == 0x8000_0000 { 366 | None 367 | } else { 368 | Some(F32(f(x.0))) 369 | } 370 | }, 371 | #[cfg(not(all(target_env = "gnu", target_os = "windows")))] 372 | () => Some(F32(f(x.0))), 373 | } 374 | 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /src/m.rs: -------------------------------------------------------------------------------- 1 | //! Bindings to libm (only for testing) 2 | 3 | extern "C" { 4 | // pub fn atan(x: f64) -> f64; 5 | // pub fn atan2(y: f64, x: f64) -> f64; 6 | // pub fn sqrt(x: f64) -> f64; 7 | pub fn atan2f(y: f32, x: f32) -> f32; 8 | pub fn atanf(x: f32) -> f32; 9 | pub fn fabs(x: f64) -> f64; 10 | pub fn fabsf(x: f32) -> f32; 11 | pub fn sqrtf(x: f32) -> f32; 12 | } 13 | -------------------------------------------------------------------------------- /src/qc.rs: -------------------------------------------------------------------------------- 1 | use std::{f32, f64, fmt}; 2 | 3 | use quickcheck::{Arbitrary, Gen}; 4 | 5 | use {FloatExt, Sign}; 6 | 7 | macro_rules! check { 8 | ($( 9 | fn $name:ident($f:ident: extern fn($($farg:ty),+) -> $fret:ty, 10 | $($arg:ident: $t:ty),+) -> Option<$ret:ty> { 11 | $($code:tt)* 12 | } 13 | )+) => { 14 | mod tests { 15 | use std::mem; 16 | 17 | use quickcheck::TestResult; 18 | 19 | use FloatExt; 20 | use qc::*; 21 | 22 | $( 23 | #[test] 24 | fn $name() { 25 | fn check($($arg: $t),+) -> TestResult { 26 | fn $name($f: extern fn($($farg),+) -> $fret, 27 | $($arg:$t),+) 28 | -> Option<$ret> { 29 | $($code)* 30 | } 31 | 32 | let our_answer = $name(super::$name, $($arg),+); 33 | let libm_f: unsafe extern "C" fn($($farg),+) -> $fret = 34 | ::m::$name; 35 | let libm_answer = 36 | $name(unsafe { mem::transmute(libm_f) }, $($arg),+); 37 | 38 | if our_answer.is_none() { 39 | return TestResult::discard(); 40 | } 41 | 42 | let our_answer = our_answer.unwrap(); 43 | let libm_answer = libm_answer.unwrap(); 44 | 45 | let print_values = || { 46 | print!("\r{} - Args: ", stringify!($name)); 47 | $(print!("{} = {:?} ", stringify!($arg), $arg);)+ 48 | print!("\n"); 49 | println!(" us: {:?}", our_answer); 50 | println!(" libm: {:?}", libm_answer); 51 | }; 52 | 53 | if !our_answer.0.eq_repr(libm_answer.0) { 54 | print_values(); 55 | TestResult::from_bool(false) 56 | } else { 57 | TestResult::from_bool(true) 58 | } 59 | } 60 | 61 | ::quickcheck::quickcheck(check as fn($($t),*) -> TestResult) 62 | } 63 | )+ 64 | } 65 | } 66 | } 67 | 68 | #[derive(Clone, Copy, PartialEq)] 69 | pub struct F32(pub f32); 70 | 71 | impl fmt::Debug for F32 { 72 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 73 | write!(f, "{} (0x{:08x})", self.0, self.0.repr()) 74 | } 75 | } 76 | 77 | #[derive(Clone, Copy, PartialEq)] 78 | pub struct F64(pub f64); 79 | 80 | impl fmt::Debug for F64 { 81 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 82 | write!(f, "{} (0x{:016x})", self.0, self.0.repr()) 83 | } 84 | } 85 | 86 | macro_rules! arbitrary_float { 87 | ($ty:ident, $fty:ident) => { 88 | impl Arbitrary for $ty { 89 | fn arbitrary(g: &mut G) -> $ty 90 | where G: Gen 91 | { 92 | let special = 93 | [-0.0, 0.0, $fty::NAN, $fty::INFINITY, $fty::NEG_INFINITY]; 94 | 95 | let (sign, mut exponent, mut significand) = g.gen(); 96 | if g.gen_weighted_bool(10) { 97 | return $ty(*g.choose(&special).unwrap()); 98 | } else if g.gen_weighted_bool(10) { 99 | // NaN variants 100 | significand = 0; 101 | } else if g.gen() { 102 | // denormalize 103 | exponent = 0; 104 | } 105 | 106 | $ty($fty::from_parts(Sign::from_bool(sign), 107 | exponent, 108 | significand)) 109 | } 110 | } 111 | } 112 | } 113 | 114 | arbitrary_float!(F32, f32); 115 | arbitrary_float!(F64, f64); 116 | --------------------------------------------------------------------------------