Compare commits

...

136 Commits

Author SHA1 Message Date
bors[bot] 2f0cffd522
Merge #99
99: Revive Float+Real in no_std thanks to libm r=cuviper a=yoanlcq

Greetings,

This is a hopeful fix for #75.  
Basically: Add `libm` as an optional dependency, and handle three possible cases depending on which features are enabled:
- std and libm: std is used;
- std and not libm: std is used;
- libm and not std: libm and FloatCore are used.

It was briefly mentioned that `libm` wasn't ready yet, but this was months ago, and I believe it is better not to wait for too long.  
If anything, bugs in `libm` should be fixed in `libm`; `num-traits` is only delegating its implementations to it; not to mention that the more `libm` is used, the likelier issues are to be found and hopefully fixed.

Thanks in advance!

Co-authored-by: Yoan Lecoq <yoanlecoq.io@gmail.com>
Co-authored-by: Josh Stone <cuviper@gmail.com>
2019-09-30 16:39:17 +00:00
bors[bot] 4fc3d8f72d
Merge #128
128: Fix unstable_name_collisions in Bounded for Wrapping r=cuviper a=cuviper



Co-authored-by: Josh Stone <cuviper@gmail.com>
2019-09-27 18:43:44 +00:00
Josh Stone 06b3f854d4 Fix unstable_name_collisions in Bounded for Wrapping 2019-09-27 11:07:25 -07:00
bors[bot] 5b9f6e4c47
Merge #127
127: Relax EPSILON comparisons in mul_add tests r=cuviper a=cuviper

Closes #124.

Co-authored-by: Josh Stone <cuviper@gmail.com>
2019-09-27 18:02:41 +00:00
Josh Stone 93328dfc90 Add libm to no_std CI 2019-09-27 10:56:03 -07:00
Josh Stone c4256bd4df Don't use libm at all with std 2019-09-27 10:53:17 -07:00
Josh Stone b64ee3809c Use a single global guard in mod real 2019-09-27 10:53:17 -07:00
Josh Stone 27b9202ff3 Raise the minimum libm
Needed for some additional methods and correct overflow behavior.
2019-09-27 10:53:17 -07:00
Yoan Lecoq f050c60df9 Reference PR 99 in README 2019-09-27 10:53:17 -07:00
Yoan Lecoq 2d113f56c8 Indicate that libm feature only builds on latest Rust 2019-09-27 10:53:17 -07:00
Yoan Lecoq 63047365be CI: make the condition for testing libm the same as u128/i128 2019-09-27 10:53:17 -07:00
Yoan Lecoq 0547a355ee Run cargo fmt --all 2019-09-27 10:53:17 -07:00
Yoan Lecoq 4b1ea5fb12 Guard Real trait definition like its blanket impl for Float 2019-09-27 10:53:17 -07:00
Yoan Lecoq 1b28e6182d Add space before triple backticks 2019-09-27 10:53:17 -07:00
Yoan Lecoq 55c5b7455a CI: test with libm 2019-09-27 10:53:17 -07:00
Yoan Lecoq c28e2fe062 Real: Ignore doc tests if Float is disabled 2019-09-27 10:53:17 -07:00
Yoan Lecoq aaf3c267bd Real: Run doc-tests only if Float is enabled 2019-09-27 10:53:17 -07:00
Yoan Lecoq 849e2a0b1b Always enable Real, feature gate Float - Real forwarding 2019-09-27 10:53:17 -07:00
Yoan Lecoq 4234eb76aa libm fallback for Pow, factorize MulAdd 2019-09-27 10:53:17 -07:00
Yoan Lecoq 4d3cb0a4ba Impl MulAdd+MulAssign with libm fallback 2019-09-27 10:53:17 -07:00
Yoan Lecoq f523f532e6 Update README 2019-09-27 10:53:17 -07:00
Yoan Lecoq b4558d1c49 Make it compile on 1.8.0 2019-09-27 10:53:17 -07:00
Yoan Lecoq fec6c3610c Revive Float+Real in no_std with libm 2019-09-27 10:53:17 -07:00
bors[bot] d394467906
Merge #135
135: Debug-panic in clamp_min/max if min/max is NAN r=cuviper a=jturner314

This also improves the docs for `clamp`, `clamp_min`, and `clamp_max`.

Closes #134.

Co-authored-by: Jim Turner <git@turner.link>
2019-09-24 20:03:31 +00:00
Jim Turner 987ed8fd38 Split clamp panicking test into separate tests 2019-09-23 22:21:33 -04:00
Jim Turner d02f166765 Restrict panic testing to when std is enabled 2019-09-23 22:14:03 -04:00
Jim Turner 33b74618b6 Debug-panic in clamp_min/max if min/max is NAN
This also improves the docs for `clamp`, `clamp_min`, and `clamp_max`.
2019-09-23 20:59:34 -04:00
bors[bot] 428f89a7d5
Merge #122
122: NAN preserving clamp_lower/upper r=cuviper a=termoshtt

`NAN` preserving lower- and upper-clamp.

Cc: https://github.com/rust-ndarray/ndarray/issues/470#issuecomment-521809782

Co-authored-by: Toshiki Teramura <toshiki.teramura@gmail.com>
2019-09-14 18:23:57 +00:00
Toshiki Teramura 0e7c2a4a00 s/less/greater/g 2019-09-14 15:59:10 +09:00
bors[bot] 3add713434
Merge #132
132: Add comments explaining why transmutes are safe r=cuviper a=Shnatsel

Add comments explaining why transmutes are safe so that people auditing unsafe code don't have to spend time figuring it out by themselves.

Co-authored-by: Sergey "Shnatsel" Davidoff <sdavydov@google.com>
2019-09-09 17:42:57 +00:00
Sergey "Shnatsel" Davidoff 40898e5071
Add comments explaining why transmutes are safe 2019-09-08 15:12:21 +02:00
Toshiki Teramura 973ba72e4f Fix doc comment 2019-08-31 15:34:40 +09:00
Toshiki Teramura e7ba9b62dc Replace lower/upper to min/max 2019-08-31 15:31:30 +09:00
Toshiki Teramura 2fb8a6e8a9 Add NaN preserving test for clamp 2019-08-31 15:14:49 +09:00
Josh Stone b8906eff1b Add i586 to CI 2019-08-30 15:54:41 -07:00
Josh Stone 7a61e79757 Relax EPSILON comparisons in mul_add tests 2019-08-30 15:24:38 -07:00
bors[bot] 45067c1357
Merge #126
126: Fix num parsing for invalid multi-byte sign chars r=cuviper a=HeroicKatora

Ensure that splitting the potential sign character from the remainder
respects UTF8 boundaries. This lets invalid characters fail correctly
with an error, instead of panicking.

Closes: #125 

Co-authored-by: Andreas Molzer <andreas.molzer@gmx.de>
2019-08-30 21:26:40 +00:00
Andreas Molzer cd0da1ae5e Fix num parsing for invalid multi-byte sign chars
Ensure that splitting the potential sign character from the remainder
respects UTF8 boundaries. This lets invalid characters fail correctly
with an error, instead of panicking.
2019-08-30 22:27:32 +02:00
Toshiki Teramura f20d74fce8 Use core::f32 instead of std::f32 2019-08-17 17:58:02 +09:00
Toshiki Teramura e8dce19146 Add clamp_{lower,upper} 2019-08-17 16:45:04 +09:00
bors[bot] 58f02a8677 Merge #119
119: Doc fixes r=cuviper a=waywardmonkeys



Co-authored-by: Bruce Mitchener <bruce.mitchener@gmail.com>
2019-07-09 17:13:44 +00:00
Bruce Mitchener d1f5658bfe Typo fixes. 2019-07-09 17:50:59 +07:00
Bruce Mitchener 107a326745 Missing backticks. 2019-07-09 17:50:50 +07:00
bors[bot] 84e14d4f36 Merge #116
116: bump autocfg to fix no_std probing r=cuviper a=cuviper



Co-authored-by: Josh Stone <cuviper@gmail.com>
2019-05-22 00:47:08 +00:00
Josh Stone 2c2cfe1bf3 Release 0.2.8 2019-05-21 17:46:08 -07:00
Josh Stone a194d91625 bump autocfg to fix no_std probing 2019-05-21 17:38:28 -07:00
bors[bot] 8e765ee1ff Merge #114
114: Release 0.2.7 r=cuviper a=cuviper



Co-authored-by: Josh Stone <cuviper@gmail.com>
2019-05-20 20:21:48 +00:00
Josh Stone 6d62b6a228 Release 0.2.7 2019-05-20 13:16:20 -07:00
bors[bot] 852ec9380f Merge #113
113: Use autocfg to probe for i128 r=cuviper a=cuviper



Co-authored-by: Josh Stone <cuviper@gmail.com>
2019-05-11 00:00:41 +00:00
Josh Stone 615d83a5b6 Use autocfg to probe for i128 2019-05-10 16:42:23 -07:00
bors[bot] 38655c91c1 Merge #110
110: Check formatting in CI r=cuviper a=cuviper



Co-authored-by: Josh Stone <cuviper@gmail.com>
2019-04-16 21:39:50 +00:00
Josh Stone a1688f6991 Check formatting in CI 2019-04-16 14:32:21 -07:00
Josh Stone c38b4b601d cargo fmt 2019-04-16 14:30:46 -07:00
bors[bot] 4ab251b0a2 Merge #108
108: int: document PrimInt trait r=cuviper a=dvdhrm

This documents the PrimInt trait, explains its intentions and features
as well as mentions its origins for future reference.

Closes #11.

Co-authored-by: David Rheinsberg <david.rheinsberg@gmail.com>
2019-03-28 20:41:04 +00:00
bors[bot] 5404658360 Merge #104
104: Add inplace methods to `Zero` and `One` r=cuviper a=lcnr

Adds the following default implemented methods to `Zero` and `One`:

```rust
fn set_zero(&mut self) {
    *self = Zero::zero();
}
```

```rust
fn set_one(&mut self) {
    *self = One::one();
}
```

This allows for reuse of BigNums.


Co-authored-by: lcnr/Bastian Kauschke <bastian_kauschke@hotmail.de>
Co-authored-by: Josh Stone <cuviper@gmail.com>
2019-03-28 19:31:51 +00:00
Josh Stone 09e27abaa0 Remove a stale doc comment on `set_zero()` 2019-03-28 12:30:22 -07:00
lcnr/Bastian Kauschke 87d4dbc418 do not return &mut Self in set_one/zero 2019-03-26 17:39:11 +01:00
David Rheinsberg 398c298fa9 int: document PrimInt trait
This documents the PrimInt trait, explains its intentions and features
as well as mentions its origins for future reference.
2019-03-15 11:16:59 +01:00
bors[bot] 8915b74ae4 Merge #105
105: fix unsigned_shr docs r=cuviper a=lcnr



Co-authored-by: lcnr/Bastian Kauschke <bastian_kauschke@hotmail.de>
2019-03-07 19:06:33 +00:00
lcnr/Bastian Kauschke 9cd2422221 fix 2019-03-07 17:22:19 +01:00
lcnr/Bastian Kauschke d2a1e035ad update 2019-03-07 17:07:07 +01:00
lcnr/Bastian Kauschke 8ac6a62a6e fix unsigned_shr docs 2019-03-06 12:47:12 +01:00
lcnr/Bastian Kauschke f06893feb0 remove mem::replace 2019-03-06 12:40:05 +01:00
lcnr/Bastian Kauschke 80052795ba fix 2019-03-05 14:45:54 +01:00
lcnr/Bastian Kauschke f3869040c7 add to_zero/one to Zero/One 2019-03-05 14:34:49 +01:00
bors[bot] d668985fae Merge #92
92: exclude CI files from crates.io r=cuviper a=ignatenkobrain



Co-authored-by: Igor Gnatenko <i.gnatenko.brain@gmail.com>
2018-10-31 19:14:02 +00:00
Igor Gnatenko ff45e00849
exclude CI files from crates.io 2018-10-31 11:24:01 +01:00
bors[bot] 2925f10f40 Merge #90
90: Fix CheckedShl/CheckedShr documentation r=cuviper a=samueltardieu

Fix #57 and more:

- CheckedShl was hinting that None was returned on overflow rather than
  on too large a rhs.
- Ditto for CheckedShr.
- CheckedShr documentation erroneously indicated that a left shift was
  going to be performed instead of a right shift.

Co-authored-by: Samuel Tardieu <sam@rfc1149.net>
2018-10-09 19:23:03 +00:00
Samuel Tardieu fc4f1afdf6 Fix CheckedShl/CheckedShr documentation
Fix #57 and more:

- CheckedShl was hinting that None was returned on overflow rather than
  on too large a rhs.
- Ditto for CheckedShr.
- CheckedShr documentation erroneously indicated that a left shift was
  going to be performed instead of a right shift.
2018-10-09 11:55:18 +02:00
bors[bot] 5c24fcc4a7 Merge #83
83: Release 0.2.6 r=cuviper a=cuviper



Co-authored-by: Josh Stone <cuviper@gmail.com>
2018-09-14 03:54:59 +00:00
Josh Stone b8ad107c1c Release 0.2.6 2018-09-13 15:45:58 -07:00
bors[bot] ac7e550b00 Merge #81
81: Add wrapping shifts r=cuviper a=edmccard

Add traits `WrappingShl` and `WrappingShr` corresponding to the
standard library `wrapping_shl` and `wrapping_shr` methods. Implement
the trait on all primitive integer types as well as on `Wrapping`.

Co-authored-by: Ed McCardell <edmccard@hotmail.com>
2018-09-13 22:29:56 +00:00
Ed McCardell abb51f9a09 Add wrapping shifts
Add traits `WrappingShl` and `WrappingShr` corresponding to the
standard library `wrapping_shl` and `wrapping_shr` methods. Implement
the trait on all primitive integer types as well as on `Wrapping`.
2018-09-02 00:51:04 -04:00
bors[bot] 5985a8b750 Merge #79
79: Updated documentation to note the pow(0, 0) case. r=cuviper a=meltinglava

Ref #78 

Co-authored-by: Roald <meltinglava>
2018-08-07 23:53:03 +00:00
Roald 2b975badfa typo 2018-08-07 12:56:16 +02:00
Roald 0f228be4d5 Updated documentation to note the pow(0, 0) case. 2018-07-21 22:23:13 +02:00
bors[bot] a415e2a751 Merge #74
74: Run cargo fmt r=cuviper a=cuviper



Co-authored-by: Josh Stone <cuviper@gmail.com>
2018-07-13 00:31:20 +00:00
Josh Stone d2bf4e04e4 Run cargo fmt 2018-07-12 17:09:22 -07:00
Josh Stone 83841d15f8 Add dates to RELEASES.md 2018-06-21 12:07:31 -07:00
bors[bot] e796afc83a Merge #73
73: Avoid `as` casts in default FromPrimitive methods r=cuviper a=cuviper

Particularly, the default `from_f64` used `n as i64`, which has
undefined behavior on overflow, kind of defeating the purpose here.
Now we use a checked `to_i64()` for this, and even try `to_u64()` as a
fallback for completeness.

Also make similar changes to default `ToPrimitive`, but at least it
didn't have the same casting problem.

Co-authored-by: Josh Stone <cuviper@gmail.com>
2018-06-20 21:26:19 +00:00
Josh Stone 714057979e Release 0.2.5 2018-06-20 14:24:56 -07:00
Josh Stone 60924ecc70 add test newtype_to_primitive 2018-06-20 13:49:57 -07:00
Josh Stone 21e3620999 doc: fix a typo, s/the/then/ 2018-06-20 13:39:08 -07:00
Josh Stone dd7900d62f Avoid closures in default `ToPrimitive` methods
In `to_f64()`, we also try `to_u64()` if `to_i64()` failed.
2018-06-20 13:10:41 -07:00
Josh Stone d968efbc76 Avoid `as` casts in default FromPrimitive methods
Particularly, the default `from_f64` used `n as i64`, which has
undefined behavior on overflow, kind of defeating the purpose here.
Now we use a checked `to_i64()` for this, and even try `to_u64()` as a
fallback for completeness.

(All of the primitive implementations already do better, at least.)
2018-06-20 13:05:03 -07:00
bors[bot] 15dc0e7127 Merge #70
70: Clarify in the docs that `mul_add` is not always faster. r=cuviper a=frewsxcv

More info:

- https://github.com/rust-lang/rust/issues/49842
- https://github.com/rust-lang/rust/pull/50572

Co-authored-by: Corey Farwell <coreyf@rwell.org>
2018-05-21 17:07:28 +00:00
Corey Farwell 4775dee66b Clarify in the docs that `mul_add` is not always faster.
More info:

- https://github.com/rust-lang/rust/issues/49842
- https://github.com/rust-lang/rust/pull/50572
2018-05-20 11:58:10 -04:00
bors[bot] f4125621ac Merge #69
69: Automatically detect support for i128/u128 r=cuviper a=cuviper



Co-authored-by: Josh Stone <cuviper@gmail.com>
2018-05-11 23:02:02 +00:00
Josh Stone c00ae2046e Release 0.2.4 2018-05-11 16:01:25 -07:00
Josh Stone 51f6c57c4b Automatically detect support for i128/u128 2018-05-11 15:50:48 -07:00
bors[bot] 4e136ddc85 Merge #68
68: Release 0.2.3 r=cuviper a=cuviper

Closes #66.

Co-authored-by: Josh Stone <cuviper@gmail.com>
2018-05-10 19:22:07 +00:00
Josh Stone 4a03db4e1c Release 0.2.3 2018-05-10 12:15:53 -07:00
bors[bot] c3feb40897 Merge #67
67: Miscellaneous tweaks regarding no_std r=cuviper a=cuviper



Co-authored-by: Josh Stone <cuviper@gmail.com>
2018-05-09 00:40:06 +00:00
Josh Stone 7e055b131f Document more impls that require std 2018-05-08 17:35:59 -07:00
Josh Stone 5add4c580e Enable all features for docs.rs 2018-05-08 17:27:44 -07:00
Josh Stone 817ef94784 Add the no-std crate category 2018-05-08 17:27:33 -07:00
Josh Stone f35cce229e Always use #![no_std], and declare std when enabled 2018-05-08 17:26:38 -07:00
bors[bot] 6edb91f5e8 Merge #60
60: i128 r=cuviper a=regexident



Co-authored-by: Vincent Esche <regexident@gmail.com>
Co-authored-by: Josh Stone <cuviper@gmail.com>
2018-05-08 21:43:10 +00:00
Josh Stone 1af2319201 add a couple more Rust versions to CI 2018-05-07 12:38:56 -07:00
Josh Stone bbbc2bd1d7 impl 128-bit numeric casts
This includes new conditional methods `ToPrimitive::{to_i128,to_u128}`
and `FromPrimitive::{from_i128,from_u128}`.  Since features can only be
additive, these methods must not cause a breaking change to anyone when
enabled -- thus they have a default implementation that converts through
64-bit values.  Types that can do better with a full 128-bit integer,
like bigint or floating-point, will probably want to override these.
2018-05-07 12:28:53 -07:00
Josh Stone 6161f1ade1 impl 128-bit MulAdd and MulAddAssign 2018-05-07 12:28:35 -07:00
Josh Stone fe53805550 impl 128-bit CheckedRem and CheckedNeg 2018-05-07 12:28:15 -07:00
Josh Stone 428e0107d2 Add imports for 128-bit Bounded 2018-05-07 12:27:42 -07:00
Josh Stone d2107ae005 There's no u64 exponent impl for i128/u128 Pow 2018-05-07 12:26:44 -07:00
Josh Stone 08ad9b1642 i128 is not Unsigned 2018-05-07 12:26:00 -07:00
Josh Stone 261efafe0b Merge branch 'master' into regexident-i128 2018-05-04 12:28:48 -07:00
bors[bot] dd67e9d2e1 Merge #61
61: Use constant for 180/π in f32::to_degrees r=cuviper a=vks

The current `f32::to_degrees` implementation uses a division to
calculate 180/π, which causes a loss of precision. Using a constant is
still not perfect (implementing a maximally-precise algorithm would come
with a high performance cost), but improves precision with a minimal
change.

This is a backport from [`std`].

[`std`]: e34c31bf02

Co-authored-by: Vinzent Steinberg <vinzent.steinberg@gmail.com>
Co-authored-by: Josh Stone <cuviper@gmail.com>
2018-05-04 19:26:46 +00:00
Josh Stone 6aaff332d3 Explicitly test FloatCore in to_degrees_rounding 2018-05-04 12:19:23 -07:00
bors[bot] a49013e338 Merge #59
59: Added `MulAdd` and `MulAddAssign` traits r=cuviper a=regexident

Both `f32` and `f64` implement fused multiply-add, which computes `(self * a) + b` with only one rounding error. This produces a more accurate result with better performance than a separate multiplication operation followed by an add:

```rust
fn mul_add(self, a: f32, b: f32) -> f32[src]
```

It is however not possible to make use of this in a generic context by abstracting over a trait.

My concrete use-case is machine learning, [gradient descent](https://en.wikipedia.org/wiki/Gradient_descent) to be specific,  
where the core operation of updating the gradient could make use of `mul_add` for both its `weights: Vector` as well as its `bias: f32`:

```rust
struct Perceptron {
  weights: Vector,
  bias: f32,
}

impl MulAdd<f32, Self> for Vector {
  // ...
}

impl Perceptron {
  fn learn(&mut self, example: Vector, expected: f32, learning_rate: f32) {
    let alpha = self.error(example, expected, learning_rate);
    self.weights = example.mul_add(alpha, self.weights);
    self.bias = self.bias.mul_add(alpha, self.bias)
  }
}
```

(The actual impl of `Vector` would be generic over its value type: `Vector<T>`, thus requiring the trait.)

Co-authored-by: Vincent Esche <regexident@gmail.com>
Co-authored-by: Josh Stone <cuviper@gmail.com>
2018-05-04 19:12:41 +00:00
Josh Stone 0d358034d9 Test MulAdd explicitly, guarded by std for floats 2018-05-04 12:09:02 -07:00
Vincent Esche 157efc5a26 Adjusted CI testing scripts 2018-04-19 09:26:02 +02:00
Vincent Esche 28be885481 Moved impl of `MulAdd`/`MulAddAssign` for `f32`/`f64` behind feature guard 2018-04-18 10:31:37 +02:00
Vincent Esche 257917f3f2 Removed inferrable value suffixes 2018-04-18 10:19:30 +02:00
Josh Stone 4fb749a401 typo: taht -> that 2018-04-13 14:29:00 -07:00
bors[bot] 4195043240 Merge #63
63: Add CheckedRem and CheckedNeg r=cuviper a=LEXUGE

Continue from #58 
I've alreadyremoved all the formats.


Co-authored-by: LEXUGE <lexugeyky@outlook.com>
Co-authored-by: Josh Stone <cuviper@gmail.com>
2018-04-13 21:25:30 +00:00
Josh Stone aa21fba9fc re-export CheckedRem and CheckedNeg at the root 2018-04-13 14:14:49 -07:00
Josh Stone b1c4074cc4 Document CheckedRem and CheckedNeg 2018-04-13 14:14:22 -07:00
Josh Stone 5fb3724b69 rename checked_impl_one_param to checked_impl_unary 2018-04-13 14:13:42 -07:00
LEXUGE f74de249c8
remove formats 2018-04-13 16:04:56 +08:00
Vinzent Steinberg 9ca219c677 Avoid test failure with Rust 1.8 by limiting to no-std builds 2018-04-10 19:56:39 +02:00
bors[bot] 97f3892bd1 Merge #62
62: Update outdated FIXME r=cuviper a=vks



Co-authored-by: Vinzent Steinberg <vinzent.steinberg@gmail.com>
2018-04-10 17:54:20 +00:00
Vinzent Steinberg 2836cfc9ab Update outdated FIXME 2018-04-10 19:51:03 +02:00
Vinzent Steinberg 6430351e74 Use constant for 180/π in f32::to_degrees
The current `f32::to_degrees` implementation uses a division to
calculate 180/π, which causes a loss of precision. Using a constant is
still not perfect (implementing a maximally-precise algorithm would come
with a high performance cost), but improves precision with a minimal
change.

This is a backport from [`std`].

[`std`]: e34c31bf02
2018-04-10 15:26:14 +02:00
Vincent Esche dd5b107c56 Added mention of `i128` feature to ‘README.md’ 2018-04-10 10:42:35 +02:00
Vincent Esche 746db74dac Added impls of `Unsigned` for `i128` and `u128` 2018-04-10 10:39:57 +02:00
Vincent Esche 152b38e03f Added impls of `Num` for `i128` and `u128` 2018-04-10 10:39:54 +02:00
Vincent Esche 6d3b55030f Added ‘i128’ feature and unit test invocation 2018-04-10 10:35:55 +02:00
Vincent Esche 830363024b Added `MulAdd` and `MulAddAssign` traits 2018-04-10 10:08:55 +02:00
Vincent Esche d1334bf903 Added impls of `Signed` for `i128` and `u128` 2018-04-09 12:58:17 +02:00
Vincent Esche f69af180cc Added impls of `Pow` for `i128` and `u128` 2018-04-09 11:11:15 +02:00
Vincent Esche 5ee2570618 Added impls of `Wrapping…` for `i128` and `u128` 2018-04-09 11:11:05 +02:00
Vincent Esche 234706fb97 Added impls of `Saturating…` for `i128` and `u128` 2018-04-09 11:10:57 +02:00
Vincent Esche b44666183d Added impls of `Checked…` for `i128` and `u128` 2018-04-09 11:10:51 +02:00
Vincent Esche bc19c34934 Added impls of `PrimInt` for `i128` and `u128` 2018-04-09 11:10:45 +02:00
Vincent Esche 62723f6f3a Added impls of `Zero` and `One` for `i128` and `u128` 2018-04-09 11:10:36 +02:00
Vincent Esche f8d1896c6c Added impls of `Bounded` for `i128` and `u128` 2018-04-09 11:10:23 +02:00
23 changed files with 1970 additions and 661 deletions

View File

@ -1,13 +1,46 @@
language: rust
sudo: false
rust:
- 1.8.0
- 1.15.0
- 1.20.0
- 1.26.0 # has_i128
- 1.31.0 # 2018!
- stable
- beta
- nightly
sudo: false
script:
- cargo build --verbose
- ./ci/test_full.sh
matrix:
include:
# i586 presents floating point challenges for lack of SSE/SSE2
- name: "i586"
rust: stable
env: TARGET=i586-unknown-linux-gnu
addons:
apt:
packages:
- gcc-multilib
before_script:
- rustup target add $TARGET
script:
- cargo test --verbose --target $TARGET --all-features
# try a target that doesn't have std at all
- name: "no_std"
rust: stable
env: TARGET=thumbv6m-none-eabi
before_script:
- rustup target add $TARGET
script:
- cargo build --verbose --target $TARGET --no-default-features --features i128
- cargo build --verbose --target $TARGET --no-default-features --features libm
- name: "rustfmt"
rust: 1.31.0
before_script:
- rustup component add rustfmt
script:
- cargo fmt --all -- --check
notifications:
email:
on_success: never

View File

@ -4,15 +4,25 @@ description = "Numeric traits for generic mathematics"
documentation = "https://docs.rs/num-traits"
homepage = "https://github.com/rust-num/num-traits"
keywords = ["mathematics", "numerics"]
categories = [ "algorithms", "science" ]
categories = ["algorithms", "science", "no-std"]
license = "MIT/Apache-2.0"
repository = "https://github.com/rust-num/num-traits"
name = "num-traits"
version = "0.2.2"
version = "0.2.8"
readme = "README.md"
build = "build.rs"
exclude = ["/ci/*", "/.travis.yml", "/bors.toml"]
[package.metadata.docs.rs]
features = ["std"]
[dependencies]
libm = { version = "0.1.4", optional = true }
[features]
default = ["std"]
std = []
i128 = []
[build-dependencies]
autocfg = "0.1.3"

View File

@ -25,16 +25,25 @@ extern crate num_traits;
## Features
This crate can be used without the standard library (`#![no_std]`) by disabling
the default `std` feature. Use this in `Cargo.toml`:
the default `std` feature. Use this in `Cargo.toml`:
```toml
[dependencies.num-traits]
version = "0.2"
default-features = false
# features = ["libm"] # <--- Uncomment if you wish to use `Float` and `Real` without `std`
```
The `Float` and `Real` traits are only available when `std` is enabled. The
`FloatCore` trait is always available.
The `Float` and `Real` traits are only available when either `std` or `libm` is enabled.
The `libm` feature is only available with Rust 1.31 and later ([see PR #99](https://github.com/rust-num/num-traits/pull/99)).
The `FloatCore` trait is always available. `MulAdd` and `MulAddAssign` for `f32`
and `f64` also require `std` or `libm`, as do implementations of signed and floating-
point exponents in `Pow`.
Implementations for `i128` and `u128` are only available with Rust 1.26 and
later. The build script automatically detects this, but you can make it
mandatory by enabling the `i128` crate feature.
## Releases

View File

@ -1,4 +1,81 @@
# Release 0.2.2
# Release 0.2.8 (2019-05-21)
- [Fixed feature detection on `no_std` targets][116].
**Contributors**: @cuviper
[116]: https://github.com/rust-num/num-traits/pull/116
# Release 0.2.7 (2019-05-20)
- [Documented when `CheckedShl` and `CheckedShr` return `None`][90].
- [The new `Zero::set_zero` and `One::set_one`][104] will set values to their
identities in place, possibly optimized better than direct assignment.
- [Documented general features and intentions of `PrimInt`][108].
**Contributors**: @cuviper, @dvdhrm, @ignatenkobrain, @lcnr, @samueltardieu
[90]: https://github.com/rust-num/num-traits/pull/90
[104]: https://github.com/rust-num/num-traits/pull/104
[108]: https://github.com/rust-num/num-traits/pull/108
# Release 0.2.6 (2018-09-13)
- [Documented that `pow(0, 0)` returns `1`][79]. Mathematically, this is not
strictly defined, but the current behavior is a pragmatic choice that has
precedent in Rust `core` for the primitives and in many other languages.
- [The new `WrappingShl` and `WrappingShr` traits][81] will wrap the shift count
if it exceeds the bit size of the type.
**Contributors**: @cuviper, @edmccard, @meltinglava
[79]: https://github.com/rust-num/num-traits/pull/79
[81]: https://github.com/rust-num/num-traits/pull/81
# Release 0.2.5 (2018-06-20)
- [Documentation for `mul_add` now clarifies that it's not always faster.][70]
- [The default methods in `FromPrimitive` and `ToPrimitive` are more robust.][73]
**Contributors**: @cuviper, @frewsxcv
[70]: https://github.com/rust-num/num-traits/pull/70
[73]: https://github.com/rust-num/num-traits/pull/73
# Release 0.2.4 (2018-05-11)
- [Support for 128-bit integers is now automatically detected and enabled.][69]
Setting the `i128` crate feature now causes the build script to panic if such
support is not detected.
**Contributors**: @cuviper
[69]: https://github.com/rust-num/num-traits/pull/69
# Release 0.2.3 (2018-05-10)
- [The new `CheckedNeg` and `CheckedRem` traits][63] perform checked `Neg` and
`Rem`, returning `Some(output)` or `None` on overflow.
- [The `no_std` implementation of `FloatCore::to_degrees` for `f32`][61] now
uses a constant for greater accuracy, mirroring [rust#47919]. (With `std` it
just calls the inherent `f32::to_degrees` in the standard library.)
- [The new `MulAdd` and `MulAddAssign` traits][59] perform a fused multiply-
add. For integer types this is just a convenience, but for floating point
types this produces a more accurate result than the separate operations.
- [All applicable traits are now implemented for 128-bit integers][60] starting
with Rust 1.26, enabled by the new `i128` crate feature. The `FromPrimitive`
and `ToPrimitive` traits now also have corresponding 128-bit methods, which
default to converting via 64-bit integers for compatibility.
**Contributors**: @cuviper, @LEXUGE, @regexident, @vks
[59]: https://github.com/rust-num/num-traits/pull/59
[60]: https://github.com/rust-num/num-traits/pull/60
[61]: https://github.com/rust-num/num-traits/pull/61
[63]: https://github.com/rust-num/num-traits/pull/63
[rust#47919]: https://github.com/rust-lang/rust/pull/47919
# Release 0.2.2 (2018-03-18)
- [Casting from floating point to integers now returns `None` on overflow][52],
avoiding [rustc's undefined behavior][rust-10184]. This applies to the `cast`
@ -10,7 +87,7 @@
[rust-10184]: https://github.com/rust-lang/rust/issues/10184
# Release 0.2.1
# Release 0.2.1 (2018-03-01)
- [The new `FloatCore` trait][32] offers a subset of `Float` for `#![no_std]` use.
[This includes everything][41] except the transcendental functions and FMA.
@ -29,7 +106,7 @@
[41]: https://github.com/rust-num/num-traits/pull/41
# Release 0.2.0
# Release 0.2.0 (2018-02-06)
- **breaking change**: [There is now a `std` feature][30], enabled by default, along
with the implication that building *without* this feature makes this a
@ -44,14 +121,14 @@
[30]: https://github.com/rust-num/num-traits/pull/30
# Release 0.1.43
# Release 0.1.43 (2018-02-06)
- All items are now [re-exported from num-traits 0.2][31] for compatibility.
[31]: https://github.com/rust-num/num-traits/pull/31
# Release 0.1.42
# Release 0.1.42 (2018-01-22)
- [num-traits now has its own source repository][num-356] at [rust-num/num-traits][home].
- [`ParseFloatError` now implements `Display`][22].

14
build.rs Normal file
View File

@ -0,0 +1,14 @@
extern crate autocfg;
use std::env;
fn main() {
let ac = autocfg::new();
if ac.probe_type("i128") {
println!("cargo:rustc-cfg=has_i128");
} else if env::var_os("CARGO_FEATURE_I128").is_some() {
panic!("i128 support was not detected!");
}
autocfg::rerun_path(file!());
}

View File

@ -5,8 +5,7 @@
set -ex
export TRAVIS_RUST_VERSION
for TRAVIS_RUST_VERSION in 1.8.0 stable beta nightly; do
for TRAVIS_RUST_VERSION in 1.8.0 1.15.0 1.20.0 stable beta nightly; do
run="rustup run $TRAVIS_RUST_VERSION"
$run cargo build --verbose
$run $PWD/ci/test_full.sh
done

View File

@ -11,3 +11,17 @@ cargo test --verbose
# test `no_std`
cargo build --verbose --no-default-features
cargo test --verbose --no-default-features
if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable)$ ]]; then
# test `i128`
cargo build --verbose --features=i128
cargo test --verbose --features=i128
# test with std and libm (libm build fails on Rust 1.26 and earlier)
cargo build --verbose --features "libm"
cargo test --verbose --features "libm"
# test `no_std` with libm (libm build fails on Rust 1.26 and earlier)
cargo build --verbose --no-default-features --features "libm"
cargo test --verbose --no-default-features --features "libm"
fi

View File

@ -1,7 +1,9 @@
use core::{usize, u8, u16, u32, u64};
use core::{isize, i8, i16, i32, i64};
use core::{f32, f64};
use core::num::Wrapping;
use core::{f32, f64};
#[cfg(has_i128)]
use core::{i128, u128};
use core::{i16, i32, i64, i8, isize};
use core::{u16, u32, u64, u8, usize};
/// Numbers which have upper and lower bounds
pub trait Bounded {
@ -16,29 +18,41 @@ macro_rules! bounded_impl {
($t:ty, $min:expr, $max:expr) => {
impl Bounded for $t {
#[inline]
fn min_value() -> $t { $min }
fn min_value() -> $t {
$min
}
#[inline]
fn max_value() -> $t { $max }
fn max_value() -> $t {
$max
}
}
}
};
}
bounded_impl!(usize, usize::MIN, usize::MAX);
bounded_impl!(u8, u8::MIN, u8::MAX);
bounded_impl!(u16, u16::MIN, u16::MAX);
bounded_impl!(u32, u32::MIN, u32::MAX);
bounded_impl!(u64, u64::MIN, u64::MAX);
bounded_impl!(u8, u8::MIN, u8::MAX);
bounded_impl!(u16, u16::MIN, u16::MAX);
bounded_impl!(u32, u32::MIN, u32::MAX);
bounded_impl!(u64, u64::MIN, u64::MAX);
#[cfg(has_i128)]
bounded_impl!(u128, u128::MIN, u128::MAX);
bounded_impl!(isize, isize::MIN, isize::MAX);
bounded_impl!(i8, i8::MIN, i8::MAX);
bounded_impl!(i16, i16::MIN, i16::MAX);
bounded_impl!(i32, i32::MIN, i32::MAX);
bounded_impl!(i64, i64::MIN, i64::MAX);
bounded_impl!(i8, i8::MIN, i8::MAX);
bounded_impl!(i16, i16::MIN, i16::MAX);
bounded_impl!(i32, i32::MIN, i32::MAX);
bounded_impl!(i64, i64::MIN, i64::MAX);
#[cfg(has_i128)]
bounded_impl!(i128, i128::MIN, i128::MAX);
impl<T: Bounded> Bounded for Wrapping<T> {
fn min_value() -> Self { Wrapping(T::min_value()) }
fn max_value() -> Self { Wrapping(T::max_value()) }
fn min_value() -> Self {
Wrapping(T::min_value())
}
fn max_value() -> Self {
Wrapping(T::max_value())
}
}
bounded_impl!(f32, f32::MIN, f32::MAX);
@ -53,9 +67,9 @@ macro_rules! for_each_tuple_ {
);
}
macro_rules! for_each_tuple {
( $m:ident ) => (
($m:ident) => {
for_each_tuple_! { $m !! A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, }
);
};
}
macro_rules! bounded_tuple {
@ -76,14 +90,13 @@ macro_rules! bounded_tuple {
for_each_tuple!(bounded_tuple);
bounded_impl!(f64, f64::MIN, f64::MAX);
#[test]
fn wrapping_bounded() {
macro_rules! test_wrapping_bounded {
($($t:ty)+) => {
$(
assert_eq!(Wrapping::<$t>::min_value().0, <$t>::min_value());
assert_eq!(Wrapping::<$t>::max_value().0, <$t>::max_value());
assert_eq!(<Wrapping<$t> as Bounded>::min_value().0, <$t>::min_value());
assert_eq!(<Wrapping<$t> as Bounded>::max_value().0, <$t>::max_value());
)+
};
}
@ -91,6 +104,21 @@ fn wrapping_bounded() {
test_wrapping_bounded!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
}
#[cfg(has_i128)]
#[test]
fn wrapping_bounded_i128() {
macro_rules! test_wrapping_bounded {
($($t:ty)+) => {
$(
assert_eq!(<Wrapping<$t> as Bounded>::min_value().0, <$t>::min_value());
assert_eq!(<Wrapping<$t> as Bounded>::max_value().0, <$t>::max_value());
)+
};
}
test_wrapping_bounded!(u128 i128);
}
#[test]
fn wrapping_is_bounded() {
fn require_bounded<T: Bounded>(_: &T) {}

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,15 @@
use core::mem;
use core::ops::Neg;
use core::num::FpCategory;
use core::ops::Neg;
use core::f32;
use core::f64;
use {Num, NumCast, ToPrimitive};
#[cfg(all(not(feature = "std"), feature = "libm"))]
use libm::{F32Ext, F64Ext};
/// Generic trait for floating point numbers that works with `no_std`.
///
/// This trait implements a subset of the `Float` trait.
@ -586,7 +589,11 @@ pub trait FloatCore: Num + NumCast + Neg<Output = Self> + PartialOrd + Copy {
if other.is_nan() {
return self;
}
if self < other { self } else { other }
if self < other {
self
} else {
other
}
}
/// Returns the maximum of the two numbers.
@ -616,7 +623,11 @@ pub trait FloatCore: Num + NumCast + Neg<Output = Self> + PartialOrd + Copy {
if other.is_nan() {
return self;
}
if self > other { self } else { other }
if self > other {
self
} else {
other
}
}
/// Returns the reciprocal (multiplicative inverse) of the number.
@ -758,6 +769,8 @@ impl FloatCore for f32 {
const EXP_MASK: u32 = 0x7f800000;
const MAN_MASK: u32 = 0x007fffff;
// Safety: this identical to the implementation of f32::to_bits(),
// which is only available starting at Rust 1.20
let bits: u32 = unsafe { mem::transmute(self) };
match (bits & MAN_MASK, bits & EXP_MASK) {
(0, 0) => FpCategory::Zero,
@ -771,7 +784,9 @@ impl FloatCore for f32 {
#[inline]
#[cfg(not(feature = "std"))]
fn to_degrees(self) -> Self {
self * (180.0 / f32::consts::PI)
// Use a constant for better precision.
const PIS_IN_180: f32 = 57.2957795130823208767981548141051703_f32;
self * PIS_IN_180
}
#[inline]
@ -828,6 +843,8 @@ impl FloatCore for f64 {
const EXP_MASK: u64 = 0x7ff0000000000000;
const MAN_MASK: u64 = 0x000fffffffffffff;
// Safety: this identical to the implementation of f64::to_bits(),
// which is only available starting at Rust 1.20
let bits: u64 = unsafe { mem::transmute(self) };
match (bits & MAN_MASK, bits & EXP_MASK) {
(0, 0) => FpCategory::Zero,
@ -841,6 +858,9 @@ impl FloatCore for f64 {
#[inline]
#[cfg(not(feature = "std"))]
fn to_degrees(self) -> Self {
// The division here is correctly rounded with respect to the true
// value of 180/π. (This differs from f32, where a constant must be
// used to ensure a correctly rounded result.)
self * (180.0 / f64::consts::PI)
}
@ -880,15 +900,9 @@ impl FloatCore for f64 {
/// Generic trait for floating point numbers
///
/// This trait is only available with the `std` feature.
#[cfg(feature = "std")]
pub trait Float
: Num
+ Copy
+ NumCast
+ PartialOrd
+ Neg<Output = Self>
{
/// This trait is only available with the `std` feature, or with the `libm` feature otherwise.
#[cfg(any(feature = "std", feature = "libm"))]
pub trait Float: Num + Copy + NumCast + PartialOrd + Neg<Output = Self> {
/// Returns the `NaN` value.
///
/// ```
@ -1232,8 +1246,10 @@ pub trait Float
fn is_sign_negative(self) -> bool;
/// Fused multiply-add. Computes `(self * a) + b` with only one rounding
/// error. This produces a more accurate result with better performance than
/// a separate multiplication operation followed by an add.
/// error, yielding a more accurate result than an unfused multiply-add.
///
/// Using `mul_add` can be more performant than an unfused multiply-add if
/// the target architecture has a dedicated `fma` CPU instruction.
///
/// ```
/// use num_traits::Float;
@ -1770,7 +1786,6 @@ pub trait Float
/// ```
fn atanh(self) -> Self;
/// Returns the mantissa, base 2 exponent, and sign as integers, respectively.
/// The original number can be recovered by `sign * mantissa * 2 ^ exponent`.
///
@ -1794,8 +1809,8 @@ pub trait Float
}
#[cfg(feature = "std")]
macro_rules! float_impl {
($T:ident $decode:ident) => (
macro_rules! float_impl_std {
($T:ident $decode:ident) => {
impl Float for $T {
constant! {
nan() -> $T::NAN;
@ -1869,16 +1884,93 @@ macro_rules! float_impl {
Self::atanh(self) -> Self;
}
}
)
};
}
#[cfg(all(not(feature = "std"), feature = "libm"))]
macro_rules! float_impl_libm {
($T:ident $decode:ident $LibmImpl:ident) => {
impl Float for $T {
constant! {
nan() -> $T::NAN;
infinity() -> $T::INFINITY;
neg_infinity() -> $T::NEG_INFINITY;
neg_zero() -> -0.0;
min_value() -> $T::MIN;
min_positive_value() -> $T::MIN_POSITIVE;
epsilon() -> $T::EPSILON;
max_value() -> $T::MAX;
}
#[inline]
#[allow(deprecated)]
fn abs_sub(self, other: Self) -> Self {
<$T as $LibmImpl>::fdim(self, other)
}
#[inline]
fn integer_decode(self) -> (u64, i16, i8) {
$decode(self)
}
forward! {
FloatCore::is_nan(self) -> bool;
FloatCore::is_infinite(self) -> bool;
FloatCore::is_finite(self) -> bool;
FloatCore::is_normal(self) -> bool;
FloatCore::classify(self) -> FpCategory;
$LibmImpl::floor(self) -> Self;
$LibmImpl::ceil(self) -> Self;
$LibmImpl::round(self) -> Self;
$LibmImpl::trunc(self) -> Self;
$LibmImpl::fract(self) -> Self;
$LibmImpl::abs(self) -> Self;
FloatCore::signum(self) -> Self;
FloatCore::is_sign_positive(self) -> bool;
FloatCore::is_sign_negative(self) -> bool;
$LibmImpl::mul_add(self, a: Self, b: Self) -> Self;
FloatCore::recip(self) -> Self;
FloatCore::powi(self, n: i32) -> Self;
$LibmImpl::powf(self, n: Self) -> Self;
$LibmImpl::sqrt(self) -> Self;
$LibmImpl::exp(self) -> Self;
$LibmImpl::exp2(self) -> Self;
$LibmImpl::ln(self) -> Self;
$LibmImpl::log(self, base: Self) -> Self;
$LibmImpl::log2(self) -> Self;
$LibmImpl::log10(self) -> Self;
FloatCore::to_degrees(self) -> Self;
FloatCore::to_radians(self) -> Self;
FloatCore::max(self, other: Self) -> Self;
FloatCore::min(self, other: Self) -> Self;
$LibmImpl::cbrt(self) -> Self;
$LibmImpl::hypot(self, other: Self) -> Self;
$LibmImpl::sin(self) -> Self;
$LibmImpl::cos(self) -> Self;
$LibmImpl::tan(self) -> Self;
$LibmImpl::asin(self) -> Self;
$LibmImpl::acos(self) -> Self;
$LibmImpl::atan(self) -> Self;
$LibmImpl::atan2(self, other: Self) -> Self;
$LibmImpl::sin_cos(self) -> (Self, Self);
$LibmImpl::exp_m1(self) -> Self;
$LibmImpl::ln_1p(self) -> Self;
$LibmImpl::sinh(self) -> Self;
$LibmImpl::cosh(self) -> Self;
$LibmImpl::tanh(self) -> Self;
$LibmImpl::asinh(self) -> Self;
$LibmImpl::acosh(self) -> Self;
$LibmImpl::atanh(self) -> Self;
}
}
};
}
fn integer_decode_f32(f: f32) -> (u64, i16, i8) {
// Safety: this identical to the implementation of f32::to_bits(),
// which is only available starting at Rust 1.20
let bits: u32 = unsafe { mem::transmute(f) };
let sign: i8 = if bits >> 31 == 0 {
1
} else {
-1
};
let sign: i8 = if bits >> 31 == 0 { 1 } else { -1 };
let mut exponent: i16 = ((bits >> 23) & 0xff) as i16;
let mantissa = if exponent == 0 {
(bits & 0x7fffff) << 1
@ -1891,12 +1983,10 @@ fn integer_decode_f32(f: f32) -> (u64, i16, i8) {
}
fn integer_decode_f64(f: f64) -> (u64, i16, i8) {
// Safety: this identical to the implementation of f64::to_bits(),
// which is only available starting at Rust 1.20
let bits: u64 = unsafe { mem::transmute(f) };
let sign: i8 = if bits >> 63 == 0 {
1
} else {
-1
};
let sign: i8 = if bits >> 63 == 0 { 1 } else { -1 };
let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16;
let mantissa = if exponent == 0 {
(bits & 0xfffffffffffff) << 1
@ -1909,9 +1999,14 @@ fn integer_decode_f64(f: f64) -> (u64, i16, i8) {
}
#[cfg(feature = "std")]
float_impl!(f32 integer_decode_f32);
float_impl_std!(f32 integer_decode_f32);
#[cfg(feature = "std")]
float_impl!(f64 integer_decode_f64);
float_impl_std!(f64 integer_decode_f64);
#[cfg(all(not(feature = "std"), feature = "libm"))]
float_impl_libm!(f32 integer_decode_f32 F32Ext);
#[cfg(all(not(feature = "std"), feature = "libm"))]
float_impl_libm!(f64 integer_decode_f64 F64Ext);
macro_rules! float_const_impl {
($(#[$doc:meta] $constant:ident,)+) => (
@ -1994,7 +2089,7 @@ mod tests {
}
}
#[cfg(feature = "std")]
#[cfg(any(feature = "std", feature = "libm"))]
#[test]
fn convert_deg_rad_std() {
for &(deg, rad) in &DEG_RAD_PAIRS {
@ -2008,4 +2103,17 @@ mod tests {
assert!((Float::to_radians(deg) - rad).abs() < 1e-5);
}
}
#[test]
// This fails with the forwarded `std` implementation in Rust 1.8.
// To avoid the failure, the test is limited to `no_std` builds.
#[cfg(not(feature = "std"))]
fn to_degrees_rounding() {
use float::FloatCore;
assert_eq!(
FloatCore::to_degrees(1_f32),
57.2957795130823208767981548141051703
);
}
}

View File

@ -1,25 +1,29 @@
use core::ops::{Add, Mul};
use core::num::Wrapping;
use core::ops::{Add, Mul};
/// Defines an additive identity element for `Self`.
///
/// # Laws
///
/// ```{.text}
/// a + 0 = a ∀ a ∈ Self
/// 0 + a = a ∀ a ∈ Self
/// ```
pub trait Zero: Sized + Add<Self, Output = Self> {
/// Returns the additive identity element of `Self`, `0`.
///
/// # Laws
///
/// ```{.text}
/// a + 0 = a ∀ a ∈ Self
/// 0 + a = a ∀ a ∈ Self
/// ```
///
/// # Purity
///
/// This function should return the same result at all times regardless of
/// external mutable state, for example values stored in TLS or in
/// `static mut`s.
// FIXME (#5527): This should be an associated constant
// This cannot be an associated constant, because of bignums.
fn zero() -> Self;
/// Sets `self` to the additive identity element of `Self`, `0`.
fn set_zero(&mut self) {
*self = Zero::zero();
}
/// Returns `true` if `self` is equal to the additive identity.
#[inline]
fn is_zero(&self) -> bool;
@ -29,64 +33,87 @@ macro_rules! zero_impl {
($t:ty, $v:expr) => {
impl Zero for $t {
#[inline]
fn zero() -> $t { $v }
fn zero() -> $t {
$v
}
#[inline]
fn is_zero(&self) -> bool { *self == $v }
fn is_zero(&self) -> bool {
*self == $v
}
}
}
};
}
zero_impl!(usize, 0usize);
zero_impl!(u8, 0u8);
zero_impl!(u16, 0u16);
zero_impl!(u32, 0u32);
zero_impl!(u64, 0u64);
zero_impl!(usize, 0);
zero_impl!(u8, 0);
zero_impl!(u16, 0);
zero_impl!(u32, 0);
zero_impl!(u64, 0);
#[cfg(has_i128)]
zero_impl!(u128, 0);
zero_impl!(isize, 0isize);
zero_impl!(i8, 0i8);
zero_impl!(i16, 0i16);
zero_impl!(i32, 0i32);
zero_impl!(i64, 0i64);
zero_impl!(isize, 0);
zero_impl!(i8, 0);
zero_impl!(i16, 0);
zero_impl!(i32, 0);
zero_impl!(i64, 0);
#[cfg(has_i128)]
zero_impl!(i128, 0);
zero_impl!(f32, 0.0f32);
zero_impl!(f64, 0.0f64);
zero_impl!(f32, 0.0);
zero_impl!(f64, 0.0);
impl<T: Zero> Zero for Wrapping<T> where Wrapping<T>: Add<Output=Wrapping<T>> {
impl<T: Zero> Zero for Wrapping<T>
where
Wrapping<T>: Add<Output = Wrapping<T>>,
{
fn is_zero(&self) -> bool {
self.0.is_zero()
}
fn set_zero(&mut self) {
self.0.set_zero();
}
fn zero() -> Self {
Wrapping(T::zero())
}
}
/// Defines a multiplicative identity element for `Self`.
///
/// # Laws
///
/// ```{.text}
/// a * 1 = a ∀ a ∈ Self
/// 1 * a = a ∀ a ∈ Self
/// ```
pub trait One: Sized + Mul<Self, Output = Self> {
/// Returns the multiplicative identity element of `Self`, `1`.
///
/// # Laws
///
/// ```{.text}
/// a * 1 = a ∀ a ∈ Self
/// 1 * a = a ∀ a ∈ Self
/// ```
///
/// # Purity
///
/// This function should return the same result at all times regardless of
/// external mutable state, for example values stored in TLS or in
/// `static mut`s.
// FIXME (#5527): This should be an associated constant
// This cannot be an associated constant, because of bignums.
fn one() -> Self;
/// Sets `self` to the multiplicative identity element of `Self`, `1`.
fn set_one(&mut self) {
*self = One::one();
}
/// Returns `true` if `self` is equal to the multiplicative identity.
///
/// For performance reasons, it's best to implement this manually.
/// After a semver bump, this method will be required, and the
/// `where Self: PartialEq` bound will be removed.
#[inline]
fn is_one(&self) -> bool where Self: PartialEq {
fn is_one(&self) -> bool
where
Self: PartialEq,
{
*self == Self::one()
}
}
@ -95,27 +122,44 @@ macro_rules! one_impl {
($t:ty, $v:expr) => {
impl One for $t {
#[inline]
fn one() -> $t { $v }
fn one() -> $t {
$v
}
#[inline]
fn is_one(&self) -> bool {
*self == $v
}
}
}
};
}
one_impl!(usize, 1usize);
one_impl!(u8, 1u8);
one_impl!(u16, 1u16);
one_impl!(u32, 1u32);
one_impl!(u64, 1u64);
one_impl!(usize, 1);
one_impl!(u8, 1);
one_impl!(u16, 1);
one_impl!(u32, 1);
one_impl!(u64, 1);
#[cfg(has_i128)]
one_impl!(u128, 1);
one_impl!(isize, 1isize);
one_impl!(i8, 1i8);
one_impl!(i16, 1i16);
one_impl!(i32, 1i32);
one_impl!(i64, 1i64);
one_impl!(isize, 1);
one_impl!(i8, 1);
one_impl!(i16, 1);
one_impl!(i32, 1);
one_impl!(i64, 1);
#[cfg(has_i128)]
one_impl!(i128, 1);
one_impl!(f32, 1.0f32);
one_impl!(f64, 1.0f64);
one_impl!(f32, 1.0);
one_impl!(f64, 1.0);
impl<T: One> One for Wrapping<T>
where
Wrapping<T>: Mul<Output = Wrapping<T>>,
{
fn set_one(&mut self) {
self.0.set_one();
}
impl<T: One> One for Wrapping<T> where Wrapping<T>: Mul<Output=Wrapping<T>> {
fn one() -> Self {
Wrapping(T::one())
}
@ -124,11 +168,16 @@ impl<T: One> One for Wrapping<T> where Wrapping<T>: Mul<Output=Wrapping<T>> {
// Some helper functions provided for backwards compatibility.
/// Returns the additive identity, `0`.
#[inline(always)] pub fn zero<T: Zero>() -> T { Zero::zero() }
#[inline(always)]
pub fn zero<T: Zero>() -> T {
Zero::zero()
}
/// Returns the multiplicative identity, `1`.
#[inline(always)] pub fn one<T: One>() -> T { One::one() }
#[inline(always)]
pub fn one<T: One>() -> T {
One::one()
}
#[test]
fn wrapping_identities() {

View File

@ -1,26 +1,55 @@
use core::ops::{Not, BitAnd, BitOr, BitXor, Shl, Shr};
use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
use {Num, NumCast};
use bounds::Bounded;
use ops::checked::*;
use ops::saturating::Saturating;
use {Num, NumCast};
pub trait PrimInt
: Sized
/// Generic trait for primitive integers.
///
/// The `PrimInt` trait is an abstraction over the builtin primitive integer types (e.g., `u8`,
/// `u32`, `isize`, `i128`, ...). It inherits the basic numeric traits and extends them with
/// bitwise operators and non-wrapping arithmetic.
///
/// The trait explicitly inherits `Copy`, `Eq`, `Ord`, and `Sized`. The intention is that all
/// types implementing this trait behave like primitive types that are passed by value by default
/// and behave like builtin integers. Furthermore, the types are expected to expose the integer
/// value in binary representation and support bitwise operators. The standard bitwise operations
/// (e.g., bitwise-and, bitwise-or, right-shift, left-shift) are inherited and the trait extends
/// these with introspective queries (e.g., `PrimInt::count_ones()`, `PrimInt::leading_zeros()`),
/// bitwise combinators (e.g., `PrimInt::rotate_left()`), and endianness converters (e.g.,
/// `PrimInt::to_be()`).
///
/// All `PrimInt` types are expected to be fixed-width binary integers. The width can be queried
/// via `T::zero().count_zeros()`. The trait currently lacks a way to query the width at
/// compile-time.
///
/// While a default implementation for all builtin primitive integers is provided, the trait is in
/// no way restricted to these. Other integer types that fulfil the requirements are free to
/// implement the trait was well.
///
/// This trait and many of the method names originate in the unstable `core::num::Int` trait from
/// the rust standard library. The original trait was never stabilized and thus removed from the
/// standard library.
pub trait PrimInt:
Sized
+ Copy
+ Num + NumCast
+ Num
+ NumCast
+ Bounded
+ PartialOrd + Ord + Eq
+ Not<Output=Self>
+ BitAnd<Output=Self>
+ BitOr<Output=Self>
+ BitXor<Output=Self>
+ Shl<usize, Output=Self>
+ Shr<usize, Output=Self>
+ CheckedAdd<Output=Self>
+ CheckedSub<Output=Self>
+ CheckedMul<Output=Self>
+ CheckedDiv<Output=Self>
+ PartialOrd
+ Ord
+ Eq
+ Not<Output = Self>
+ BitAnd<Output = Self>
+ BitOr<Output = Self>
+ BitXor<Output = Self>
+ Shl<usize, Output = Self>
+ Shr<usize, Output = Self>
+ CheckedAdd<Output = Self>
+ CheckedSub<Output = Self>
+ CheckedMul<Output = Self>
+ CheckedDiv<Output = Self>
+ Saturating
{
/// Returns the number of ones in the binary representation of `self`.
@ -168,10 +197,10 @@ pub trait PrimInt
/// ```
/// use num_traits::PrimInt;
///
/// let n = 0xFEDCBA9876543210i64;
/// let m = 0x000FEDCBA9876543i64;
/// let n = -8i8; // 0b11111000
/// let m = 62i8; // 0b00111110
///
/// assert_eq!(n.unsigned_shr(12), m);
/// assert_eq!(n.unsigned_shr(2), m);
/// ```
fn unsigned_shr(self, n: u32) -> Self;
@ -278,7 +307,7 @@ pub trait PrimInt
}
macro_rules! prim_int_impl {
($T:ty, $S:ty, $U:ty) => (
($T:ty, $S:ty, $U:ty) => {
impl PrimInt for $T {
#[inline]
fn count_ones(self) -> u32 {
@ -360,17 +389,21 @@ macro_rules! prim_int_impl {
<$T>::pow(self, exp)
}
}
)
};
}
// prim_int_impl!(type, signed, unsigned);
prim_int_impl!(u8, i8, u8);
prim_int_impl!(u16, i16, u16);
prim_int_impl!(u32, i32, u32);
prim_int_impl!(u64, i64, u64);
prim_int_impl!(u8, i8, u8);
prim_int_impl!(u16, i16, u16);
prim_int_impl!(u32, i32, u32);
prim_int_impl!(u64, i64, u64);
#[cfg(has_i128)]
prim_int_impl!(u128, i128, u128);
prim_int_impl!(usize, isize, usize);
prim_int_impl!(i8, i8, u8);
prim_int_impl!(i16, i16, u16);
prim_int_impl!(i32, i32, u32);
prim_int_impl!(i64, i64, u64);
prim_int_impl!(i8, i8, u8);
prim_int_impl!(i16, i16, u16);
prim_int_impl!(i32, i32, u32);
prim_int_impl!(i64, i64, u64);
#[cfg(has_i128)]
prim_int_impl!(i128, i128, u128);
prim_int_impl!(isize, isize, usize);

View File

@ -15,51 +15,54 @@
//! The `num-traits` crate is tested for rustc 1.8 and greater.
#![doc(html_root_url = "https://docs.rs/num-traits/0.2")]
#![deny(unconditional_recursion)]
#![cfg_attr(not(feature = "std"), no_std)]
#![no_std]
#[cfg(feature = "std")]
extern crate core;
extern crate std;
// Only `no_std` builds actually use `libm`.
#[cfg(all(not(feature = "std"), feature = "libm"))]
extern crate libm;
use core::ops::{Add, Sub, Mul, Div, Rem};
use core::ops::{AddAssign, SubAssign, MulAssign, DivAssign, RemAssign};
use core::num::Wrapping;
use core::fmt;
use core::num::Wrapping;
use core::ops::{Add, Div, Mul, Rem, Sub};
use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign};
pub use bounds::Bounded;
#[cfg(feature = "std")]
#[cfg(any(feature = "std", feature = "libm"))]
pub use float::Float;
pub use float::FloatConst;
// pub use real::{FloatCore, Real}; // NOTE: Don't do this, it breaks `use num_traits::*;`.
pub use identities::{Zero, One, zero, one};
pub use ops::inv::Inv;
pub use ops::checked::{CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedShl, CheckedShr};
pub use ops::wrapping::{WrappingAdd, WrappingMul, WrappingSub};
pub use ops::saturating::Saturating;
pub use sign::{Signed, Unsigned, abs, abs_sub, signum};
pub use cast::{AsPrimitive, FromPrimitive, ToPrimitive, NumCast, cast};
pub use cast::{cast, AsPrimitive, FromPrimitive, NumCast, ToPrimitive};
pub use identities::{one, zero, One, Zero};
pub use int::PrimInt;
pub use pow::{Pow, pow, checked_pow};
pub use ops::checked::{
CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, CheckedSub,
};
pub use ops::inv::Inv;
pub use ops::mul_add::{MulAdd, MulAddAssign};
pub use ops::saturating::Saturating;
pub use ops::wrapping::{WrappingAdd, WrappingMul, WrappingShl, WrappingShr, WrappingSub};
pub use pow::{checked_pow, pow, Pow};
pub use sign::{abs, abs_sub, signum, Signed, Unsigned};
#[macro_use]
mod macros;
pub mod identities;
pub mod sign;
pub mod ops;
pub mod bounds;
pub mod float;
#[cfg(feature = "std")]
pub mod real;
pub mod cast;
pub mod float;
pub mod identities;
pub mod int;
pub mod ops;
pub mod pow;
pub mod real;
pub mod sign;
/// The base trait for numeric types, covering `0` and `1` values,
/// comparisons, basic numeric operations, and string conversion.
pub trait Num: PartialEq + Zero + One + NumOps
{
pub trait Num: PartialEq + Zero + One + NumOps {
type FromStrRadixErr;
/// Convert from a string and radix <= 36.
@ -81,21 +84,23 @@ pub trait Num: PartialEq + Zero + One + NumOps
/// The trait for types implementing basic numeric operations
///
/// This is automatically implemented for types which implement the operators.
pub trait NumOps<Rhs = Self, Output = Self>
: Add<Rhs, Output = Output>
pub trait NumOps<Rhs = Self, Output = Self>:
Add<Rhs, Output = Output>
+ Sub<Rhs, Output = Output>
+ Mul<Rhs, Output = Output>
+ Div<Rhs, Output = Output>
+ Rem<Rhs, Output = Output>
{}
{
}
impl<T, Rhs, Output> NumOps<Rhs, Output> for T
where T: Add<Rhs, Output = Output>
+ Sub<Rhs, Output = Output>
+ Mul<Rhs, Output = Output>
+ Div<Rhs, Output = Output>
+ Rem<Rhs, Output = Output>
{}
impl<T, Rhs, Output> NumOps<Rhs, Output> for T where
T: Add<Rhs, Output = Output>
+ Sub<Rhs, Output = Output>
+ Mul<Rhs, Output = Output>
+ Div<Rhs, Output = Output>
+ Rem<Rhs, Output = Output>
{
}
/// The trait for `Num` types which also implement numeric operations taking
/// the second operand by reference.
@ -114,21 +119,15 @@ impl<T, Base> RefNum<Base> for T where T: NumOps<Base, Base> + for<'r> NumOps<&'
/// The trait for types implementing numeric assignment operators (like `+=`).
///
/// This is automatically implemented for types which implement the operators.
pub trait NumAssignOps<Rhs = Self>
: AddAssign<Rhs>
+ SubAssign<Rhs>
+ MulAssign<Rhs>
+ DivAssign<Rhs>
+ RemAssign<Rhs>
{}
pub trait NumAssignOps<Rhs = Self>:
AddAssign<Rhs> + SubAssign<Rhs> + MulAssign<Rhs> + DivAssign<Rhs> + RemAssign<Rhs>
{
}
impl<T, Rhs> NumAssignOps<Rhs> for T
where T: AddAssign<Rhs>
+ SubAssign<Rhs>
+ MulAssign<Rhs>
+ DivAssign<Rhs>
+ RemAssign<Rhs>
{}
impl<T, Rhs> NumAssignOps<Rhs> for T where
T: AddAssign<Rhs> + SubAssign<Rhs> + MulAssign<Rhs> + DivAssign<Rhs> + RemAssign<Rhs>
{
}
/// The trait for `Num` types which also implement assignment operators.
///
@ -143,7 +142,6 @@ impl<T> NumAssign for T where T: Num + NumAssignOps {}
pub trait NumAssignRef: NumAssign + for<'r> NumAssignOps<&'r Self> {}
impl<T> NumAssignRef for T where T: NumAssign + for<'r> NumAssignOps<&'r T> {}
macro_rules! int_trait_impl {
($name:ident for $($t:ty)*) => ($(
impl $name for $t {
@ -158,11 +156,16 @@ macro_rules! int_trait_impl {
)*)
}
int_trait_impl!(Num for usize u8 u16 u32 u64 isize i8 i16 i32 i64);
#[cfg(has_i128)]
int_trait_impl!(Num for u128 i128);
impl<T: Num> Num for Wrapping<T>
where Wrapping<T>:
Add<Output = Wrapping<T>> + Sub<Output = Wrapping<T>>
+ Mul<Output = Wrapping<T>> + Div<Output = Wrapping<T>> + Rem<Output = Wrapping<T>>
where
Wrapping<T>: Add<Output = Wrapping<T>>
+ Sub<Output = Wrapping<T>>
+ Mul<Output = Wrapping<T>>
+ Div<Output = Wrapping<T>>
+ Rem<Output = Wrapping<T>>,
{
type FromStrRadixErr = T::FromStrRadixErr;
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
@ -170,7 +173,6 @@ impl<T: Num> Num for Wrapping<T>
}
}
#[derive(Debug)]
pub enum FloatErrorKind {
Empty,
@ -217,7 +219,12 @@ macro_rules! float_trait_impl {
}
fn slice_shift_char(src: &str) -> Option<(char, &str)> {
src.chars().nth(0).map(|ch| (ch, &src[1..]))
let mut chars = src.chars();
if let Some(ch) = chars.next() {
Some((ch, chars.as_str()))
} else {
None
}
}
let (is_positive, src) = match slice_shift_char(src) {
@ -360,6 +367,8 @@ float_trait_impl!(Num for f32 f64);
/// If input is less than min then this returns min.
/// If input is greater than max then this returns max.
/// Otherwise this returns input.
///
/// **Panics** in debug mode if `!(min <= max)`.
#[inline]
pub fn clamp<T: PartialOrd>(input: T, min: T, max: T) -> T {
debug_assert!(min <= max, "min must be less than or equal to max");
@ -372,17 +381,97 @@ pub fn clamp<T: PartialOrd>(input: T, min: T, max: T) -> T {
}
}
/// A value bounded by a minimum value
///
/// If input is less than min then this returns min.
/// Otherwise this returns input.
/// `clamp_min(std::f32::NAN, 1.0)` preserves `NAN` different from `f32::min(std::f32::NAN, 1.0)`.
///
/// **Panics** in debug mode if `!(min == min)`. (This occurs if `min` is `NAN`.)
#[inline]
pub fn clamp_min<T: PartialOrd>(input: T, min: T) -> T {
debug_assert!(min == min, "min must not be NAN");
if input < min {
min
} else {
input
}
}
/// A value bounded by a maximum value
///
/// If input is greater than max then this returns max.
/// Otherwise this returns input.
/// `clamp_max(std::f32::NAN, 1.0)` preserves `NAN` different from `f32::max(std::f32::NAN, 1.0)`.
///
/// **Panics** in debug mode if `!(max == max)`. (This occurs if `max` is `NAN`.)
#[inline]
pub fn clamp_max<T: PartialOrd>(input: T, max: T) -> T {
debug_assert!(max == max, "max must not be NAN");
if input > max {
max
} else {
input
}
}
#[test]
fn clamp_test() {
// Int test
assert_eq!(1, clamp(1, -1, 2));
assert_eq!(-1, clamp(-2, -1, 2));
assert_eq!(2, clamp(3, -1, 2));
assert_eq!(1, clamp_min(1, -1));
assert_eq!(-1, clamp_min(-2, -1));
assert_eq!(-1, clamp_max(1, -1));
assert_eq!(-2, clamp_max(-2, -1));
// Float test
assert_eq!(1.0, clamp(1.0, -1.0, 2.0));
assert_eq!(-1.0, clamp(-2.0, -1.0, 2.0));
assert_eq!(2.0, clamp(3.0, -1.0, 2.0));
assert_eq!(1.0, clamp_min(1.0, -1.0));
assert_eq!(-1.0, clamp_min(-2.0, -1.0));
assert_eq!(-1.0, clamp_max(1.0, -1.0));
assert_eq!(-2.0, clamp_max(-2.0, -1.0));
assert!(clamp(::core::f32::NAN, -1.0, 1.0).is_nan());
assert!(clamp_min(::core::f32::NAN, 1.0).is_nan());
assert!(clamp_max(::core::f32::NAN, 1.0).is_nan());
}
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn clamp_nan_min() {
clamp(0., ::core::f32::NAN, 1.);
}
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn clamp_nan_max() {
clamp(0., -1., ::core::f32::NAN);
}
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn clamp_nan_min_max() {
clamp(0., ::core::f32::NAN, ::core::f32::NAN);
}
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn clamp_min_nan_min() {
clamp_min(0., ::core::f32::NAN);
}
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn clamp_max_nan_max() {
clamp_max(0., ::core::f32::NAN);
}
#[test]
@ -396,6 +485,15 @@ fn from_str_radix_unwrap() {
assert_eq!(f, 0.0);
}
#[test]
fn from_str_radix_multi_byte_fail() {
// Ensure parsing doesn't panic, even on invalid sign characters
assert!(f32::from_str_radix("™0.2", 10).is_err());
// Even when parsing the exponent sign
assert!(f32::from_str_radix("0.2E™1", 10).is_err());
}
#[test]
fn wrapping_is_num() {
fn require_num<T: Num>(_: &T) {}
@ -438,7 +536,8 @@ fn check_numref_ops() {
#[test]
fn check_refnum_ops() {
fn compute<T: Copy>(x: &T, y: T) -> T
where for<'a> &'a T: RefNum<T>
where
for<'a> &'a T: RefNum<T>,
{
&(&(&(&(x * y) / y) % y) + y) - y
}
@ -448,7 +547,8 @@ fn check_refnum_ops() {
#[test]
fn check_refref_ops() {
fn compute<T>(x: &T, y: &T) -> T
where for<'a> &'a T: RefNum<T>
where
for<'a> &'a T: RefNum<T>,
{
&(&(&(&(x * y) / y) % y) + y) - y
}

View File

@ -1,8 +1,8 @@
use core::ops::{Add, Sub, Mul, Div, Shl, Shr};
use core::ops::{Add, Div, Mul, Rem, Shl, Shr, Sub};
/// Performs addition that returns `None` instead of wrapping around on
/// overflow.
pub trait CheckedAdd: Sized + Add<Self, Output=Self> {
pub trait CheckedAdd: Sized + Add<Self, Output = Self> {
/// Adds two numbers, checking for overflow. If overflow happens, `None` is
/// returned.
fn checked_add(&self, v: &Self) -> Option<Self>;
@ -16,7 +16,7 @@ macro_rules! checked_impl {
<$t>::$method(*self, *v)
}
}
}
};
}
checked_impl!(CheckedAdd, checked_add, u8);
@ -24,15 +24,19 @@ checked_impl!(CheckedAdd, checked_add, u16);
checked_impl!(CheckedAdd, checked_add, u32);
checked_impl!(CheckedAdd, checked_add, u64);
checked_impl!(CheckedAdd, checked_add, usize);
#[cfg(has_i128)]
checked_impl!(CheckedAdd, checked_add, u128);
checked_impl!(CheckedAdd, checked_add, i8);
checked_impl!(CheckedAdd, checked_add, i16);
checked_impl!(CheckedAdd, checked_add, i32);
checked_impl!(CheckedAdd, checked_add, i64);
checked_impl!(CheckedAdd, checked_add, isize);
#[cfg(has_i128)]
checked_impl!(CheckedAdd, checked_add, i128);
/// Performs subtraction that returns `None` instead of wrapping around on underflow.
pub trait CheckedSub: Sized + Sub<Self, Output=Self> {
pub trait CheckedSub: Sized + Sub<Self, Output = Self> {
/// Subtracts two numbers, checking for underflow. If underflow happens,
/// `None` is returned.
fn checked_sub(&self, v: &Self) -> Option<Self>;
@ -43,16 +47,20 @@ checked_impl!(CheckedSub, checked_sub, u16);
checked_impl!(CheckedSub, checked_sub, u32);
checked_impl!(CheckedSub, checked_sub, u64);
checked_impl!(CheckedSub, checked_sub, usize);
#[cfg(has_i128)]
checked_impl!(CheckedSub, checked_sub, u128);
checked_impl!(CheckedSub, checked_sub, i8);
checked_impl!(CheckedSub, checked_sub, i16);
checked_impl!(CheckedSub, checked_sub, i32);
checked_impl!(CheckedSub, checked_sub, i64);
checked_impl!(CheckedSub, checked_sub, isize);
#[cfg(has_i128)]
checked_impl!(CheckedSub, checked_sub, i128);
/// Performs multiplication that returns `None` instead of wrapping around on underflow or
/// overflow.
pub trait CheckedMul: Sized + Mul<Self, Output=Self> {
pub trait CheckedMul: Sized + Mul<Self, Output = Self> {
/// Multiplies two numbers, checking for underflow or overflow. If underflow
/// or overflow happens, `None` is returned.
fn checked_mul(&self, v: &Self) -> Option<Self>;
@ -63,16 +71,20 @@ checked_impl!(CheckedMul, checked_mul, u16);
checked_impl!(CheckedMul, checked_mul, u32);
checked_impl!(CheckedMul, checked_mul, u64);
checked_impl!(CheckedMul, checked_mul, usize);
#[cfg(has_i128)]
checked_impl!(CheckedMul, checked_mul, u128);
checked_impl!(CheckedMul, checked_mul, i8);
checked_impl!(CheckedMul, checked_mul, i16);
checked_impl!(CheckedMul, checked_mul, i32);
checked_impl!(CheckedMul, checked_mul, i64);
checked_impl!(CheckedMul, checked_mul, isize);
#[cfg(has_i128)]
checked_impl!(CheckedMul, checked_mul, i128);
/// Performs division that returns `None` instead of panicking on division by zero and instead of
/// wrapping around on underflow and overflow.
pub trait CheckedDiv: Sized + Div<Self, Output=Self> {
pub trait CheckedDiv: Sized + Div<Self, Output = Self> {
/// Divides two numbers, checking for underflow, overflow and division by
/// zero. If any of that happens, `None` is returned.
fn checked_div(&self, v: &Self) -> Option<Self>;
@ -83,17 +95,111 @@ checked_impl!(CheckedDiv, checked_div, u16);
checked_impl!(CheckedDiv, checked_div, u32);
checked_impl!(CheckedDiv, checked_div, u64);
checked_impl!(CheckedDiv, checked_div, usize);
#[cfg(has_i128)]
checked_impl!(CheckedDiv, checked_div, u128);
checked_impl!(CheckedDiv, checked_div, i8);
checked_impl!(CheckedDiv, checked_div, i16);
checked_impl!(CheckedDiv, checked_div, i32);
checked_impl!(CheckedDiv, checked_div, i64);
checked_impl!(CheckedDiv, checked_div, isize);
#[cfg(has_i128)]
checked_impl!(CheckedDiv, checked_div, i128);
/// Performs a left shift that returns `None` on overflow.
pub trait CheckedShl: Sized + Shl<u32, Output=Self> {
/// Shifts a number to the left, checking for overflow. If overflow happens,
/// `None` is returned.
/// Performs an integral remainder that returns `None` instead of panicking on division by zero and
/// instead of wrapping around on underflow and overflow.
pub trait CheckedRem: Sized + Rem<Self, Output = Self> {
/// Finds the remainder of dividing two numbers, checking for underflow, overflow and division
/// by zero. If any of that happens, `None` is returned.
///
/// # Examples
///
/// ```
/// use num_traits::CheckedRem;
/// use std::i32::MIN;
///
/// assert_eq!(CheckedRem::checked_rem(&10, &7), Some(3));
/// assert_eq!(CheckedRem::checked_rem(&10, &-7), Some(3));
/// assert_eq!(CheckedRem::checked_rem(&-10, &7), Some(-3));
/// assert_eq!(CheckedRem::checked_rem(&-10, &-7), Some(-3));
///
/// assert_eq!(CheckedRem::checked_rem(&10, &0), None);
///
/// assert_eq!(CheckedRem::checked_rem(&MIN, &1), Some(0));
/// assert_eq!(CheckedRem::checked_rem(&MIN, &-1), None);
/// ```
fn checked_rem(&self, v: &Self) -> Option<Self>;
}
checked_impl!(CheckedRem, checked_rem, u8);
checked_impl!(CheckedRem, checked_rem, u16);
checked_impl!(CheckedRem, checked_rem, u32);
checked_impl!(CheckedRem, checked_rem, u64);
checked_impl!(CheckedRem, checked_rem, usize);
#[cfg(has_i128)]
checked_impl!(CheckedRem, checked_rem, u128);
checked_impl!(CheckedRem, checked_rem, i8);
checked_impl!(CheckedRem, checked_rem, i16);
checked_impl!(CheckedRem, checked_rem, i32);
checked_impl!(CheckedRem, checked_rem, i64);
checked_impl!(CheckedRem, checked_rem, isize);
#[cfg(has_i128)]
checked_impl!(CheckedRem, checked_rem, i128);
macro_rules! checked_impl_unary {
($trait_name:ident, $method:ident, $t:ty) => {
impl $trait_name for $t {
#[inline]
fn $method(&self) -> Option<$t> {
<$t>::$method(*self)
}
}
};
}
/// Performs negation that returns `None` if the result can't be represented.
pub trait CheckedNeg: Sized {
/// Negates a number, returning `None` for results that can't be represented, like signed `MIN`
/// values that can't be positive, or non-zero unsigned values that can't be negative.
///
/// # Examples
///
/// ```
/// use num_traits::CheckedNeg;
/// use std::i32::MIN;
///
/// assert_eq!(CheckedNeg::checked_neg(&1_i32), Some(-1));
/// assert_eq!(CheckedNeg::checked_neg(&-1_i32), Some(1));
/// assert_eq!(CheckedNeg::checked_neg(&MIN), None);
///
/// assert_eq!(CheckedNeg::checked_neg(&0_u32), Some(0));
/// assert_eq!(CheckedNeg::checked_neg(&1_u32), None);
/// ```
fn checked_neg(&self) -> Option<Self>;
}
checked_impl_unary!(CheckedNeg, checked_neg, u8);
checked_impl_unary!(CheckedNeg, checked_neg, u16);
checked_impl_unary!(CheckedNeg, checked_neg, u32);
checked_impl_unary!(CheckedNeg, checked_neg, u64);
checked_impl_unary!(CheckedNeg, checked_neg, usize);
#[cfg(has_i128)]
checked_impl_unary!(CheckedNeg, checked_neg, u128);
checked_impl_unary!(CheckedNeg, checked_neg, i8);
checked_impl_unary!(CheckedNeg, checked_neg, i16);
checked_impl_unary!(CheckedNeg, checked_neg, i32);
checked_impl_unary!(CheckedNeg, checked_neg, i64);
checked_impl_unary!(CheckedNeg, checked_neg, isize);
#[cfg(has_i128)]
checked_impl_unary!(CheckedNeg, checked_neg, i128);
/// Performs a left shift that returns `None` on shifts larger than
/// the type width.
pub trait CheckedShl: Sized + Shl<u32, Output = Self> {
/// Checked shift left. Computes `self << rhs`, returning `None`
/// if `rhs` is larger than or equal to the number of bits in `self`.
///
/// ```
/// use num_traits::CheckedShl;
@ -116,7 +222,7 @@ macro_rules! checked_shift_impl {
<$t>::$method(*self, rhs)
}
}
}
};
}
checked_shift_impl!(CheckedShl, checked_shl, u8);
@ -124,17 +230,22 @@ checked_shift_impl!(CheckedShl, checked_shl, u16);
checked_shift_impl!(CheckedShl, checked_shl, u32);
checked_shift_impl!(CheckedShl, checked_shl, u64);
checked_shift_impl!(CheckedShl, checked_shl, usize);
#[cfg(has_i128)]
checked_shift_impl!(CheckedShl, checked_shl, u128);
checked_shift_impl!(CheckedShl, checked_shl, i8);
checked_shift_impl!(CheckedShl, checked_shl, i16);
checked_shift_impl!(CheckedShl, checked_shl, i32);
checked_shift_impl!(CheckedShl, checked_shl, i64);
checked_shift_impl!(CheckedShl, checked_shl, isize);
#[cfg(has_i128)]
checked_shift_impl!(CheckedShl, checked_shl, i128);
/// Performs a right shift that returns `None` on overflow.
pub trait CheckedShr: Sized + Shr<u32, Output=Self> {
/// Shifts a number to the left, checking for overflow. If overflow happens,
/// `None` is returned.
/// Performs a right shift that returns `None` on shifts larger than
/// the type width.
pub trait CheckedShr: Sized + Shr<u32, Output = Self> {
/// Checked shift right. Computes `self >> rhs`, returning `None`
/// if `rhs` is larger than or equal to the number of bits in `self`.
///
/// ```
/// use num_traits::CheckedShr;
@ -154,9 +265,13 @@ checked_shift_impl!(CheckedShr, checked_shr, u16);
checked_shift_impl!(CheckedShr, checked_shr, u32);
checked_shift_impl!(CheckedShr, checked_shr, u64);
checked_shift_impl!(CheckedShr, checked_shr, usize);
#[cfg(has_i128)]
checked_shift_impl!(CheckedShr, checked_shr, u128);
checked_shift_impl!(CheckedShr, checked_shr, i8);
checked_shift_impl!(CheckedShr, checked_shr, i16);
checked_shift_impl!(CheckedShr, checked_shr, i32);
checked_shift_impl!(CheckedShr, checked_shr, i64);
checked_shift_impl!(CheckedShr, checked_shr, isize);
#[cfg(has_i128)]
checked_shift_impl!(CheckedShr, checked_shr, i128);

View File

@ -20,20 +20,28 @@ pub trait Inv {
impl Inv for f32 {
type Output = f32;
#[inline]
fn inv(self) -> f32 { 1.0 / self }
fn inv(self) -> f32 {
1.0 / self
}
}
impl Inv for f64 {
type Output = f64;
#[inline]
fn inv(self) -> f64 { 1.0 / self }
fn inv(self) -> f64 {
1.0 / self
}
}
impl<'a> Inv for &'a f32 {
type Output = f32;
#[inline]
fn inv(self) -> f32 { 1.0 / *self }
fn inv(self) -> f32 {
1.0 / *self
}
}
impl<'a> Inv for &'a f64 {
type Output = f64;
#[inline]
fn inv(self) -> f64 { 1.0 / *self }
fn inv(self) -> f64 {
1.0 / *self
}
}

View File

@ -1,4 +1,5 @@
pub mod saturating;
pub mod checked;
pub mod wrapping;
pub mod inv;
pub mod mul_add;
pub mod saturating;
pub mod wrapping;

151
src/ops/mul_add.rs Normal file
View File

@ -0,0 +1,151 @@
/// Fused multiply-add. Computes `(self * a) + b` with only one rounding
/// error, yielding a more accurate result than an unfused multiply-add.
///
/// Using `mul_add` can be more performant than an unfused multiply-add if
/// the target architecture has a dedicated `fma` CPU instruction.
///
/// Note that `A` and `B` are `Self` by default, but this is not mandatory.
///
/// # Example
///
/// ```
/// use std::f32;
///
/// let m = 10.0_f32;
/// let x = 4.0_f32;
/// let b = 60.0_f32;
///
/// // 100.0
/// let abs_difference = (m.mul_add(x, b) - (m*x + b)).abs();
///
/// assert!(abs_difference <= 100.0 * f32::EPSILON);
/// ```
pub trait MulAdd<A = Self, B = Self> {
/// The resulting type after applying the fused multiply-add.
type Output;
/// Performs the fused multiply-add operation.
fn mul_add(self, a: A, b: B) -> Self::Output;
}
/// The fused multiply-add assignment operation.
pub trait MulAddAssign<A = Self, B = Self> {
/// Performs the fused multiply-add operation.
fn mul_add_assign(&mut self, a: A, b: B);
}
#[cfg(any(feature = "std", feature = "libm"))]
impl MulAdd<f32, f32> for f32 {
type Output = Self;
#[inline]
fn mul_add(self, a: Self, b: Self) -> Self::Output {
<Self as ::Float>::mul_add(self, a, b)
}
}
#[cfg(any(feature = "std", feature = "libm"))]
impl MulAdd<f64, f64> for f64 {
type Output = Self;
#[inline]
fn mul_add(self, a: Self, b: Self) -> Self::Output {
<Self as ::Float>::mul_add(self, a, b)
}
}
macro_rules! mul_add_impl {
($trait_name:ident for $($t:ty)*) => {$(
impl $trait_name for $t {
type Output = Self;
#[inline]
fn mul_add(self, a: Self, b: Self) -> Self::Output {
(self * a) + b
}
}
)*}
}
mul_add_impl!(MulAdd for isize usize i8 u8 i16 u16 i32 u32 i64 u64);
#[cfg(has_i128)]
mul_add_impl!(MulAdd for i128 u128);
#[cfg(any(feature = "std", feature = "libm"))]
impl MulAddAssign<f32, f32> for f32 {
#[inline]
fn mul_add_assign(&mut self, a: Self, b: Self) {
*self = <Self as ::Float>::mul_add(*self, a, b)
}
}
#[cfg(any(feature = "std", feature = "libm"))]
impl MulAddAssign<f64, f64> for f64 {
#[inline]
fn mul_add_assign(&mut self, a: Self, b: Self) {
*self = <Self as ::Float>::mul_add(*self, a, b)
}
}
macro_rules! mul_add_assign_impl {
($trait_name:ident for $($t:ty)*) => {$(
impl $trait_name for $t {
#[inline]
fn mul_add_assign(&mut self, a: Self, b: Self) {
*self = (*self * a) + b
}
}
)*}
}
mul_add_assign_impl!(MulAddAssign for isize usize i8 u8 i16 u16 i32 u32 i64 u64);
#[cfg(has_i128)]
mul_add_assign_impl!(MulAddAssign for i128 u128);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mul_add_integer() {
macro_rules! test_mul_add {
($($t:ident)+) => {
$(
{
let m: $t = 2;
let x: $t = 3;
let b: $t = 4;
assert_eq!(MulAdd::mul_add(m, x, b), (m*x + b));
}
)+
};
}
test_mul_add!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
}
#[test]
#[cfg(feature = "std")]
fn mul_add_float() {
macro_rules! test_mul_add {
($($t:ident)+) => {
$(
{
use core::$t;
let m: $t = 12.0;
let x: $t = 3.4;
let b: $t = 5.6;
let abs_difference = (MulAdd::mul_add(m, x, b) - (m*x + b)).abs();
assert!(abs_difference <= 46.4 * $t::EPSILON);
}
)+
};
}
test_mul_add!(f32 f64);
}
}

View File

@ -26,3 +26,5 @@ macro_rules! saturating_impl {
}
saturating_impl!(Saturating for isize usize i8 u8 i16 u16 i32 u32 i64 u64);
#[cfg(has_i128)]
saturating_impl!(Saturating for i128 u128);

View File

@ -1,5 +1,5 @@
use core::ops::{Add, Sub, Mul};
use core::num::Wrapping;
use core::ops::{Add, Mul, Shl, Shr, Sub};
macro_rules! wrapping_impl {
($trait_name:ident, $method:ident, $t:ty) => {
@ -17,11 +17,11 @@ macro_rules! wrapping_impl {
<$t>::$method(*self, *v)
}
}
}
};
}
/// Performs addition that wraps around on overflow.
pub trait WrappingAdd: Sized + Add<Self, Output=Self> {
pub trait WrappingAdd: Sized + Add<Self, Output = Self> {
/// Wrapping (modular) addition. Computes `self + other`, wrapping around at the boundary of
/// the type.
fn wrapping_add(&self, v: &Self) -> Self;
@ -32,15 +32,19 @@ wrapping_impl!(WrappingAdd, wrapping_add, u16);
wrapping_impl!(WrappingAdd, wrapping_add, u32);
wrapping_impl!(WrappingAdd, wrapping_add, u64);
wrapping_impl!(WrappingAdd, wrapping_add, usize);
#[cfg(has_i128)]
wrapping_impl!(WrappingAdd, wrapping_add, u128);
wrapping_impl!(WrappingAdd, wrapping_add, i8);
wrapping_impl!(WrappingAdd, wrapping_add, i16);
wrapping_impl!(WrappingAdd, wrapping_add, i32);
wrapping_impl!(WrappingAdd, wrapping_add, i64);
wrapping_impl!(WrappingAdd, wrapping_add, isize);
#[cfg(has_i128)]
wrapping_impl!(WrappingAdd, wrapping_add, i128);
/// Performs subtraction that wraps around on overflow.
pub trait WrappingSub: Sized + Sub<Self, Output=Self> {
pub trait WrappingSub: Sized + Sub<Self, Output = Self> {
/// Wrapping (modular) subtraction. Computes `self - other`, wrapping around at the boundary
/// of the type.
fn wrapping_sub(&self, v: &Self) -> Self;
@ -51,15 +55,19 @@ wrapping_impl!(WrappingSub, wrapping_sub, u16);
wrapping_impl!(WrappingSub, wrapping_sub, u32);
wrapping_impl!(WrappingSub, wrapping_sub, u64);
wrapping_impl!(WrappingSub, wrapping_sub, usize);
#[cfg(has_i128)]
wrapping_impl!(WrappingSub, wrapping_sub, u128);
wrapping_impl!(WrappingSub, wrapping_sub, i8);
wrapping_impl!(WrappingSub, wrapping_sub, i16);
wrapping_impl!(WrappingSub, wrapping_sub, i32);
wrapping_impl!(WrappingSub, wrapping_sub, i64);
wrapping_impl!(WrappingSub, wrapping_sub, isize);
#[cfg(has_i128)]
wrapping_impl!(WrappingSub, wrapping_sub, i128);
/// Performs multiplication that wraps around on overflow.
pub trait WrappingMul: Sized + Mul<Self, Output=Self> {
pub trait WrappingMul: Sized + Mul<Self, Output = Self> {
/// Wrapping (modular) multiplication. Computes `self * other`, wrapping around at the boundary
/// of the type.
fn wrapping_mul(&self, v: &Self) -> Self;
@ -70,42 +78,167 @@ wrapping_impl!(WrappingMul, wrapping_mul, u16);
wrapping_impl!(WrappingMul, wrapping_mul, u32);
wrapping_impl!(WrappingMul, wrapping_mul, u64);
wrapping_impl!(WrappingMul, wrapping_mul, usize);
#[cfg(has_i128)]
wrapping_impl!(WrappingMul, wrapping_mul, u128);
wrapping_impl!(WrappingMul, wrapping_mul, i8);
wrapping_impl!(WrappingMul, wrapping_mul, i16);
wrapping_impl!(WrappingMul, wrapping_mul, i32);
wrapping_impl!(WrappingMul, wrapping_mul, i64);
wrapping_impl!(WrappingMul, wrapping_mul, isize);
#[cfg(has_i128)]
wrapping_impl!(WrappingMul, wrapping_mul, i128);
macro_rules! wrapping_shift_impl {
($trait_name:ident, $method:ident, $t:ty) => {
impl $trait_name for $t {
#[inline]
fn $method(&self, rhs: u32) -> $t {
<$t>::$method(*self, rhs)
}
}
};
}
/// Performs a left shift that does not panic.
pub trait WrappingShl: Sized + Shl<usize, Output = Self> {
/// Panic-free bitwise shift-left; yields `self << mask(rhs)`,
/// where `mask` removes any high order bits of `rhs` that would
/// cause the shift to exceed the bitwidth of the type.
///
/// ```
/// use num_traits::WrappingShl;
///
/// let x: u16 = 0x0001;
///
/// assert_eq!(WrappingShl::wrapping_shl(&x, 0), 0x0001);
/// assert_eq!(WrappingShl::wrapping_shl(&x, 1), 0x0002);
/// assert_eq!(WrappingShl::wrapping_shl(&x, 15), 0x8000);
/// assert_eq!(WrappingShl::wrapping_shl(&x, 16), 0x0001);
/// ```
fn wrapping_shl(&self, rhs: u32) -> Self;
}
wrapping_shift_impl!(WrappingShl, wrapping_shl, u8);
wrapping_shift_impl!(WrappingShl, wrapping_shl, u16);
wrapping_shift_impl!(WrappingShl, wrapping_shl, u32);
wrapping_shift_impl!(WrappingShl, wrapping_shl, u64);
wrapping_shift_impl!(WrappingShl, wrapping_shl, usize);
#[cfg(has_i128)]
wrapping_shift_impl!(WrappingShl, wrapping_shl, u128);
wrapping_shift_impl!(WrappingShl, wrapping_shl, i8);
wrapping_shift_impl!(WrappingShl, wrapping_shl, i16);
wrapping_shift_impl!(WrappingShl, wrapping_shl, i32);
wrapping_shift_impl!(WrappingShl, wrapping_shl, i64);
wrapping_shift_impl!(WrappingShl, wrapping_shl, isize);
#[cfg(has_i128)]
wrapping_shift_impl!(WrappingShl, wrapping_shl, i128);
/// Performs a right shift that does not panic.
pub trait WrappingShr: Sized + Shr<usize, Output = Self> {
/// Panic-free bitwise shift-right; yields `self >> mask(rhs)`,
/// where `mask` removes any high order bits of `rhs` that would
/// cause the shift to exceed the bitwidth of the type.
///
/// ```
/// use num_traits::WrappingShr;
///
/// let x: u16 = 0x8000;
///
/// assert_eq!(WrappingShr::wrapping_shr(&x, 0), 0x8000);
/// assert_eq!(WrappingShr::wrapping_shr(&x, 1), 0x4000);
/// assert_eq!(WrappingShr::wrapping_shr(&x, 15), 0x0001);
/// assert_eq!(WrappingShr::wrapping_shr(&x, 16), 0x8000);
/// ```
fn wrapping_shr(&self, rhs: u32) -> Self;
}
wrapping_shift_impl!(WrappingShr, wrapping_shr, u8);
wrapping_shift_impl!(WrappingShr, wrapping_shr, u16);
wrapping_shift_impl!(WrappingShr, wrapping_shr, u32);
wrapping_shift_impl!(WrappingShr, wrapping_shr, u64);
wrapping_shift_impl!(WrappingShr, wrapping_shr, usize);
#[cfg(has_i128)]
wrapping_shift_impl!(WrappingShr, wrapping_shr, u128);
wrapping_shift_impl!(WrappingShr, wrapping_shr, i8);
wrapping_shift_impl!(WrappingShr, wrapping_shr, i16);
wrapping_shift_impl!(WrappingShr, wrapping_shr, i32);
wrapping_shift_impl!(WrappingShr, wrapping_shr, i64);
wrapping_shift_impl!(WrappingShr, wrapping_shr, isize);
#[cfg(has_i128)]
wrapping_shift_impl!(WrappingShr, wrapping_shr, i128);
// Well this is a bit funny, but all the more appropriate.
impl<T: WrappingAdd> WrappingAdd for Wrapping<T> where Wrapping<T>: Add<Output = Wrapping<T>> {
impl<T: WrappingAdd> WrappingAdd for Wrapping<T>
where
Wrapping<T>: Add<Output = Wrapping<T>>,
{
fn wrapping_add(&self, v: &Self) -> Self {
Wrapping(self.0.wrapping_add(&v.0))
}
}
impl<T: WrappingSub> WrappingSub for Wrapping<T> where Wrapping<T>: Sub<Output = Wrapping<T>> {
impl<T: WrappingSub> WrappingSub for Wrapping<T>
where
Wrapping<T>: Sub<Output = Wrapping<T>>,
{
fn wrapping_sub(&self, v: &Self) -> Self {
Wrapping(self.0.wrapping_sub(&v.0))
}
}
impl<T: WrappingMul> WrappingMul for Wrapping<T> where Wrapping<T>: Mul<Output = Wrapping<T>> {
impl<T: WrappingMul> WrappingMul for Wrapping<T>
where
Wrapping<T>: Mul<Output = Wrapping<T>>,
{
fn wrapping_mul(&self, v: &Self) -> Self {
Wrapping(self.0.wrapping_mul(&v.0))
}
}
impl<T: WrappingShl> WrappingShl for Wrapping<T>
where
Wrapping<T>: Shl<usize, Output = Wrapping<T>>,
{
fn wrapping_shl(&self, rhs: u32) -> Self {
Wrapping(self.0.wrapping_shl(rhs))
}
}
impl<T: WrappingShr> WrappingShr for Wrapping<T>
where
Wrapping<T>: Shr<usize, Output = Wrapping<T>>,
{
fn wrapping_shr(&self, rhs: u32) -> Self {
Wrapping(self.0.wrapping_shr(rhs))
}
}
#[test]
fn test_wrapping_traits() {
fn wrapping_add<T: WrappingAdd>(a: T, b: T) -> T { a.wrapping_add(&b) }
fn wrapping_sub<T: WrappingSub>(a: T, b: T) -> T { a.wrapping_sub(&b) }
fn wrapping_mul<T: WrappingMul>(a: T, b: T) -> T { a.wrapping_mul(&b) }
fn wrapping_add<T: WrappingAdd>(a: T, b: T) -> T {
a.wrapping_add(&b)
}
fn wrapping_sub<T: WrappingSub>(a: T, b: T) -> T {
a.wrapping_sub(&b)
}
fn wrapping_mul<T: WrappingMul>(a: T, b: T) -> T {
a.wrapping_mul(&b)
}
fn wrapping_shl<T: WrappingShl>(a: T, b: u32) -> T {
a.wrapping_shl(b)
}
fn wrapping_shr<T: WrappingShr>(a: T, b: u32) -> T {
a.wrapping_shr(b)
}
assert_eq!(wrapping_add(255, 1), 0u8);
assert_eq!(wrapping_sub(0, 1), 255u8);
assert_eq!(wrapping_mul(255, 2), 254u8);
assert_eq!(wrapping_shl(255, 8), 255u8);
assert_eq!(wrapping_shr(255, 8), 255u8);
assert_eq!(wrapping_add(255, 1), (Wrapping(255u8) + Wrapping(1u8)).0);
assert_eq!(wrapping_sub(0, 1), (Wrapping(0u8) - Wrapping(1u8)).0);
assert_eq!(wrapping_mul(255, 2), (Wrapping(255u8) * Wrapping(2u8)).0);
assert_eq!(wrapping_shl(255, 8), (Wrapping(255u8) << 8).0);
assert_eq!(wrapping_shr(255, 8), (Wrapping(255u8) >> 8).0);
}
#[test]
@ -125,3 +258,15 @@ fn wrapping_is_wrappingmul() {
fn require_wrappingmul<T: WrappingMul>(_: &T) {}
require_wrappingmul(&Wrapping(42));
}
#[test]
fn wrapping_is_wrappingshl() {
fn require_wrappingshl<T: WrappingShl>(_: &T) {}
require_wrappingshl(&Wrapping(42));
}
#[test]
fn wrapping_is_wrappingshr() {
fn require_wrappingshr<T: WrappingShr>(_: &T) {}
require_wrappingshr(&Wrapping(42));
}

View File

@ -1,6 +1,6 @@
use core::ops::Mul;
use core::num::Wrapping;
use {One, CheckedMul};
use core::ops::Mul;
use {CheckedMul, One};
/// Binary operator for raising a value to a power.
pub trait Pow<RHS> {
@ -98,6 +98,25 @@ pow_impl!(i64, u8, u32, i64::pow);
pow_impl!(i64, u16, u32, i64::pow);
pow_impl!(i64, u32, u32, i64::pow);
pow_impl!(i64, usize);
#[cfg(has_i128)]
pow_impl!(u128, u8, u32, u128::pow);
#[cfg(has_i128)]
pow_impl!(u128, u16, u32, u128::pow);
#[cfg(has_i128)]
pow_impl!(u128, u32, u32, u128::pow);
#[cfg(has_i128)]
pow_impl!(u128, usize);
#[cfg(has_i128)]
pow_impl!(i128, u8, u32, i128::pow);
#[cfg(has_i128)]
pow_impl!(i128, u16, u32, i128::pow);
#[cfg(has_i128)]
pow_impl!(i128, u32, u32, i128::pow);
#[cfg(has_i128)]
pow_impl!(i128, usize);
pow_impl!(usize, u8, u32, usize::pow);
pow_impl!(usize, u16, u32, usize::pow);
pow_impl!(usize, u32, u32, usize::pow);
@ -114,6 +133,10 @@ pow_impl!(Wrapping<u32>);
pow_impl!(Wrapping<i32>);
pow_impl!(Wrapping<u64>);
pow_impl!(Wrapping<i64>);
#[cfg(has_i128)]
pow_impl!(Wrapping<u128>);
#[cfg(has_i128)]
pow_impl!(Wrapping<i128>);
pow_impl!(Wrapping<usize>);
pow_impl!(Wrapping<isize>);
@ -129,27 +152,30 @@ pow_impl!(Wrapping<isize>);
// pow_impl!(usize, u64);
// pow_impl!(isize, u64);
#[cfg(feature = "std")]
#[cfg(any(feature = "std", feature = "libm"))]
mod float_impls {
use super::Pow;
use Float;
pow_impl!(f32, i8, i32, f32::powi);
pow_impl!(f32, u8, i32, f32::powi);
pow_impl!(f32, i16, i32, f32::powi);
pow_impl!(f32, u16, i32, f32::powi);
pow_impl!(f32, i32, i32, f32::powi);
pow_impl!(f64, i8, i32, f64::powi);
pow_impl!(f64, u8, i32, f64::powi);
pow_impl!(f64, i16, i32, f64::powi);
pow_impl!(f64, u16, i32, f64::powi);
pow_impl!(f64, i32, i32, f64::powi);
pow_impl!(f32, f32, f32, f32::powf);
pow_impl!(f64, f32, f64, f64::powf);
pow_impl!(f64, f64, f64, f64::powf);
pow_impl!(f32, i8, i32, <f32 as Float>::powi);
pow_impl!(f32, u8, i32, <f32 as Float>::powi);
pow_impl!(f32, i16, i32, <f32 as Float>::powi);
pow_impl!(f32, u16, i32, <f32 as Float>::powi);
pow_impl!(f32, i32, i32, <f32 as Float>::powi);
pow_impl!(f64, i8, i32, <f64 as Float>::powi);
pow_impl!(f64, u8, i32, <f64 as Float>::powi);
pow_impl!(f64, i16, i32, <f64 as Float>::powi);
pow_impl!(f64, u16, i32, <f64 as Float>::powi);
pow_impl!(f64, i32, i32, <f64 as Float>::powi);
pow_impl!(f32, f32, f32, <f32 as Float>::powf);
pow_impl!(f64, f32, f64, <f64 as Float>::powf);
pow_impl!(f64, f64, f64, <f64 as Float>::powf);
}
/// Raises a value to the power of exp, using exponentiation by squaring.
///
/// Note that `0⁰` (`pow(0, 0)`) returns `1`. Mathematically this is undefined.
///
/// # Example
///
/// ```rust
@ -157,16 +183,21 @@ mod float_impls {
///
/// assert_eq!(pow(2i8, 4), 16);
/// assert_eq!(pow(6u8, 3), 216);
/// assert_eq!(pow(0u8, 0), 1); // Be aware if this case affects you
/// ```
#[inline]
pub fn pow<T: Clone + One + Mul<T, Output = T>>(mut base: T, mut exp: usize) -> T {
if exp == 0 { return T::one() }
if exp == 0 {
return T::one();
}
while exp & 1 == 0 {
base = base.clone() * base;
exp >>= 1;
}
if exp == 1 { return base }
if exp == 1 {
return base;
}
let mut acc = base.clone();
while exp > 1 {
@ -181,6 +212,8 @@ pub fn pow<T: Clone + One + Mul<T, Output = T>>(mut base: T, mut exp: usize) ->
/// Raises a value to the power of exp, returning `None` if an overflow occurred.
///
/// Note that `0⁰` (`checked_pow(0, 0)`) returns `Some(1)`. Mathematically this is undefined.
///
/// Otherwise same as the `pow` function.
///
/// # Example
@ -191,22 +224,31 @@ pub fn pow<T: Clone + One + Mul<T, Output = T>>(mut base: T, mut exp: usize) ->
/// assert_eq!(checked_pow(2i8, 4), Some(16));
/// assert_eq!(checked_pow(7i8, 8), None);
/// assert_eq!(checked_pow(7u32, 8), Some(5_764_801));
/// assert_eq!(checked_pow(0u32, 0), Some(1)); // Be aware if this case affect you
/// ```
#[inline]
pub fn checked_pow<T: Clone + One + CheckedMul>(mut base: T, mut exp: usize) -> Option<T> {
if exp == 0 { return Some(T::one()) }
if exp == 0 {
return Some(T::one());
}
macro_rules! optry {
( $ expr : expr ) => {
if let Some(val) = $expr { val } else { return None }
}
($expr:expr) => {
if let Some(val) = $expr {
val
} else {
return None;
}
};
}
while exp & 1 == 0 {
base = optry!(base.checked_mul(&base));
exp >>= 1;
}
if exp == 1 { return Some(base) }
if exp == 1 {
return Some(base);
}
let mut acc = base.clone();
while exp > 1 {

View File

@ -1,6 +1,8 @@
use std::ops::Neg;
#![cfg(any(feature = "std", feature = "libm"))]
use {Num, NumCast, Float};
use core::ops::Neg;
use {Float, Num, NumCast};
// NOTE: These doctests have the same issue as those in src/float.rs.
// They're testing the inherent methods directly, and not those of `Real`.
@ -11,14 +13,8 @@ use {Num, NumCast, Float};
/// See [this Wikipedia article](https://en.wikipedia.org/wiki/Real_data_type)
/// for a list of data types that could meaningfully implement this trait.
///
/// This trait is only available with the `std` feature.
pub trait Real
: Num
+ Copy
+ NumCast
+ PartialOrd
+ Neg<Output = Self>
{
/// This trait is only available with the `std` feature, or with the `libm` feature otherwise.
pub trait Real: Num + Copy + NumCast + PartialOrd + Neg<Output = Self> {
/// Returns the smallest finite value that this type can represent.
///
/// ```
@ -215,8 +211,10 @@ pub trait Real
fn is_sign_negative(self) -> bool;
/// Fused multiply-add. Computes `(self * a) + b` with only one rounding
/// error. This produces a more accurate result with better performance than
/// a separate multiplication operation followed by an add.
/// error, yielding a more accurate result than an unfused multiply-add.
///
/// Using `mul_add` can be more performant than an unfused multiply-add if
/// the target architecture has a dedicated `fma` CPU instruction.
///
/// ```
/// use num_traits::real::Real;

View File

@ -1,8 +1,8 @@
use core::ops::Neg;
use core::num::Wrapping;
use core::ops::Neg;
use Num;
use float::FloatCore;
use Num;
/// Useful functions for signed numbers (i.e. numbers that can be negative).
pub trait Signed: Sized + Num + Neg<Output = Self> {
@ -74,7 +74,12 @@ macro_rules! signed_impl {
signed_impl!(isize i8 i16 i32 i64);
impl<T: Signed> Signed for Wrapping<T> where Wrapping<T>: Num + Neg<Output=Wrapping<T>>
#[cfg(has_i128)]
signed_impl!(i128);
impl<T: Signed> Signed for Wrapping<T>
where
Wrapping<T>: Num + Neg<Output = Wrapping<T>>,
{
#[inline]
fn abs(&self) -> Self {
@ -92,10 +97,14 @@ impl<T: Signed> Signed for Wrapping<T> where Wrapping<T>: Num + Neg<Output=Wrapp
}
#[inline]
fn is_positive(&self) -> bool { self.0.is_positive() }
fn is_positive(&self) -> bool {
self.0.is_positive()
}
#[inline]
fn is_negative(&self) -> bool { self.0.is_negative() }
fn is_negative(&self) -> bool {
self.0.is_negative()
}
}
macro_rules! signed_float_impl {
@ -112,7 +121,11 @@ macro_rules! signed_float_impl {
/// and `other` is returned.
#[inline]
fn abs_sub(&self, other: &$t) -> $t {
if *self <= *other { 0. } else { *self - *other }
if *self <= *other {
0.
} else {
*self - *other
}
}
/// # Returns
@ -127,13 +140,17 @@ macro_rules! signed_float_impl {
/// Returns `true` if the number is positive, including `+0.0` and `INFINITY`
#[inline]
fn is_positive(&self) -> bool { FloatCore::is_sign_positive(*self) }
fn is_positive(&self) -> bool {
FloatCore::is_sign_positive(*self)
}
/// Returns `true` if the number is negative, including `-0.0` and `NEG_INFINITY`
#[inline]
fn is_negative(&self) -> bool { FloatCore::is_sign_negative(*self) }
fn is_negative(&self) -> bool {
FloatCore::is_sign_negative(*self)
}
}
}
};
}
signed_float_impl!(f32);
@ -171,7 +188,10 @@ pub fn abs_sub<T: Signed>(x: T, y: T) -> T {
/// * `0` if the number is zero
/// * `1` if the number is positive
/// * `-1` if the number is negative
#[inline(always)] pub fn signum<T: Signed>(value: T) -> T { value.signum() }
#[inline(always)]
pub fn signum<T: Signed>(value: T) -> T {
value.signum()
}
/// A trait for values which cannot be negative
pub trait Unsigned: Num {}
@ -183,6 +203,8 @@ macro_rules! empty_trait_impl {
}
empty_trait_impl!(Unsigned for usize u8 u16 u32 u64);
#[cfg(has_i128)]
empty_trait_impl!(Unsigned for u128);
impl<T: Unsigned> Unsigned for Wrapping<T> where Wrapping<T>: Num {}

396
tests/cast.rs Normal file
View File

@ -0,0 +1,396 @@
//! Tests of `num_traits::cast`.
#![no_std]
#[cfg(feature = "std")]
#[macro_use]
extern crate std;
extern crate num_traits;
use num_traits::cast::*;
use num_traits::Bounded;
use core::{f32, f64};
#[cfg(has_i128)]
use core::{i128, u128};
use core::{i16, i32, i64, i8, isize};
use core::{u16, u32, u64, u8, usize};
use core::fmt::Debug;
use core::mem;
use core::num::Wrapping;
#[test]
fn to_primitive_float() {
let f32_toolarge = 1e39f64;
assert_eq!(f32_toolarge.to_f32(), None);
assert_eq!((f32::MAX as f64).to_f32(), Some(f32::MAX));
assert_eq!((-f32::MAX as f64).to_f32(), Some(-f32::MAX));
assert_eq!(f64::INFINITY.to_f32(), Some(f32::INFINITY));
assert_eq!((f64::NEG_INFINITY).to_f32(), Some(f32::NEG_INFINITY));
assert!((f64::NAN).to_f32().map_or(false, |f| f.is_nan()));
}
#[test]
fn wrapping_to_primitive() {
macro_rules! test_wrapping_to_primitive {
($($t:ty)+) => {
$({
let i: $t = 0;
let w = Wrapping(i);
assert_eq!(i.to_u8(), w.to_u8());
assert_eq!(i.to_u16(), w.to_u16());
assert_eq!(i.to_u32(), w.to_u32());
assert_eq!(i.to_u64(), w.to_u64());
assert_eq!(i.to_usize(), w.to_usize());
assert_eq!(i.to_i8(), w.to_i8());
assert_eq!(i.to_i16(), w.to_i16());
assert_eq!(i.to_i32(), w.to_i32());
assert_eq!(i.to_i64(), w.to_i64());
assert_eq!(i.to_isize(), w.to_isize());
assert_eq!(i.to_f32(), w.to_f32());
assert_eq!(i.to_f64(), w.to_f64());
})+
};
}
test_wrapping_to_primitive!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
}
#[test]
fn wrapping_is_toprimitive() {
fn require_toprimitive<T: ToPrimitive>(_: &T) {}
require_toprimitive(&Wrapping(42));
}
#[test]
fn wrapping_is_fromprimitive() {
fn require_fromprimitive<T: FromPrimitive>(_: &T) {}
require_fromprimitive(&Wrapping(42));
}
#[test]
fn wrapping_is_numcast() {
fn require_numcast<T: NumCast>(_: &T) {}
require_numcast(&Wrapping(42));
}
#[test]
fn as_primitive() {
let x: f32 = (1.625f64).as_();
assert_eq!(x, 1.625f32);
let x: f32 = (3.14159265358979323846f64).as_();
assert_eq!(x, 3.1415927f32);
let x: u8 = (768i16).as_();
assert_eq!(x, 0);
}
#[test]
fn float_to_integer_checks_overflow() {
// This will overflow an i32
let source: f64 = 1.0e+123f64;
// Expect the overflow to be caught
assert_eq!(cast::<f64, i32>(source), None);
}
#[test]
fn cast_to_int_checks_overflow() {
let big_f: f64 = 1.0e123;
let normal_f: f64 = 1.0;
let small_f: f64 = -1.0e123;
assert_eq!(None, cast::<f64, isize>(big_f));
assert_eq!(None, cast::<f64, i8>(big_f));
assert_eq!(None, cast::<f64, i16>(big_f));
assert_eq!(None, cast::<f64, i32>(big_f));
assert_eq!(None, cast::<f64, i64>(big_f));
assert_eq!(Some(normal_f as isize), cast::<f64, isize>(normal_f));
assert_eq!(Some(normal_f as i8), cast::<f64, i8>(normal_f));
assert_eq!(Some(normal_f as i16), cast::<f64, i16>(normal_f));
assert_eq!(Some(normal_f as i32), cast::<f64, i32>(normal_f));
assert_eq!(Some(normal_f as i64), cast::<f64, i64>(normal_f));
assert_eq!(None, cast::<f64, isize>(small_f));
assert_eq!(None, cast::<f64, i8>(small_f));
assert_eq!(None, cast::<f64, i16>(small_f));
assert_eq!(None, cast::<f64, i32>(small_f));
assert_eq!(None, cast::<f64, i64>(small_f));
}
#[test]
fn cast_to_unsigned_int_checks_overflow() {
let big_f: f64 = 1.0e123;
let normal_f: f64 = 1.0;
let small_f: f64 = -1.0e123;
assert_eq!(None, cast::<f64, usize>(big_f));
assert_eq!(None, cast::<f64, u8>(big_f));
assert_eq!(None, cast::<f64, u16>(big_f));
assert_eq!(None, cast::<f64, u32>(big_f));
assert_eq!(None, cast::<f64, u64>(big_f));
assert_eq!(Some(normal_f as usize), cast::<f64, usize>(normal_f));
assert_eq!(Some(normal_f as u8), cast::<f64, u8>(normal_f));
assert_eq!(Some(normal_f as u16), cast::<f64, u16>(normal_f));
assert_eq!(Some(normal_f as u32), cast::<f64, u32>(normal_f));
assert_eq!(Some(normal_f as u64), cast::<f64, u64>(normal_f));
assert_eq!(None, cast::<f64, usize>(small_f));
assert_eq!(None, cast::<f64, u8>(small_f));
assert_eq!(None, cast::<f64, u16>(small_f));
assert_eq!(None, cast::<f64, u32>(small_f));
assert_eq!(None, cast::<f64, u64>(small_f));
}
#[test]
#[cfg(has_i128)]
fn cast_to_i128_checks_overflow() {
let big_f: f64 = 1.0e123;
let normal_f: f64 = 1.0;
let small_f: f64 = -1.0e123;
assert_eq!(None, cast::<f64, i128>(big_f));
assert_eq!(None, cast::<f64, u128>(big_f));
assert_eq!(Some(normal_f as i128), cast::<f64, i128>(normal_f));
assert_eq!(Some(normal_f as u128), cast::<f64, u128>(normal_f));
assert_eq!(None, cast::<f64, i128>(small_f));
assert_eq!(None, cast::<f64, u128>(small_f));
}
#[cfg(feature = "std")]
fn dbg(args: ::core::fmt::Arguments) {
println!("{}", args);
}
#[cfg(not(feature = "std"))]
fn dbg(_: ::core::fmt::Arguments) {}
// Rust 1.8 doesn't handle cfg on macros correctly
macro_rules! dbg { ($($tok:tt)*) => { dbg(format_args!($($tok)*)) } }
macro_rules! float_test_edge {
($f:ident -> $($t:ident)+) => { $({
dbg!("testing cast edge cases for {} -> {}", stringify!($f), stringify!($t));
let small = if $t::MIN == 0 || mem::size_of::<$t>() < mem::size_of::<$f>() {
$t::MIN as $f - 1.0
} else {
($t::MIN as $f).raw_offset(1).floor()
};
let fmin = small.raw_offset(-1);
dbg!(" testing min {}\n\tvs. {:.0}\n\tand {:.0}", $t::MIN, fmin, small);
assert_eq!(Some($t::MIN), cast::<$f, $t>($t::MIN as $f));
assert_eq!(Some($t::MIN), cast::<$f, $t>(fmin));
assert_eq!(None, cast::<$f, $t>(small));
let (max, large) = if mem::size_of::<$t>() < mem::size_of::<$f>() {
($t::MAX, $t::MAX as $f + 1.0)
} else {
let large = $t::MAX as $f; // rounds up!
let max = large.raw_offset(-1) as $t; // the next smallest possible
assert_eq!(max.count_ones(), $f::MANTISSA_DIGITS);
(max, large)
};
let fmax = large.raw_offset(-1);
dbg!(" testing max {}\n\tvs. {:.0}\n\tand {:.0}", max, fmax, large);
assert_eq!(Some(max), cast::<$f, $t>(max as $f));
assert_eq!(Some(max), cast::<$f, $t>(fmax));
assert_eq!(None, cast::<$f, $t>(large));
dbg!(" testing non-finite values");
assert_eq!(None, cast::<$f, $t>($f::NAN));
assert_eq!(None, cast::<$f, $t>($f::INFINITY));
assert_eq!(None, cast::<$f, $t>($f::NEG_INFINITY));
})+}
}
trait RawOffset: Sized {
type Raw;
fn raw_offset(self, offset: Self::Raw) -> Self;
}
impl RawOffset for f32 {
type Raw = i32;
fn raw_offset(self, offset: Self::Raw) -> Self {
unsafe {
let raw: Self::Raw = mem::transmute(self);
mem::transmute(raw + offset)
}
}
}
impl RawOffset for f64 {
type Raw = i64;
fn raw_offset(self, offset: Self::Raw) -> Self {
unsafe {
let raw: Self::Raw = mem::transmute(self);
mem::transmute(raw + offset)
}
}
}
#[test]
fn cast_float_to_int_edge_cases() {
float_test_edge!(f32 -> isize i8 i16 i32 i64);
float_test_edge!(f32 -> usize u8 u16 u32 u64);
float_test_edge!(f64 -> isize i8 i16 i32 i64);
float_test_edge!(f64 -> usize u8 u16 u32 u64);
}
#[test]
#[cfg(has_i128)]
fn cast_float_to_i128_edge_cases() {
float_test_edge!(f32 -> i128 u128);
float_test_edge!(f64 -> i128 u128);
}
macro_rules! int_test_edge {
($f:ident -> { $($t:ident)+ } with $BigS:ident $BigU:ident ) => { $({
fn test_edge() {
dbg!("testing cast edge cases for {} -> {}", stringify!($f), stringify!($t));
match ($f::MIN as $BigS).cmp(&($t::MIN as $BigS)) {
Greater => {
assert_eq!(Some($f::MIN as $t), cast::<$f, $t>($f::MIN));
}
Equal => {
assert_eq!(Some($t::MIN), cast::<$f, $t>($f::MIN));
}
Less => {
let min = $t::MIN as $f;
assert_eq!(Some($t::MIN), cast::<$f, $t>(min));
assert_eq!(None, cast::<$f, $t>(min - 1));
}
}
match ($f::MAX as $BigU).cmp(&($t::MAX as $BigU)) {
Greater => {
let max = $t::MAX as $f;
assert_eq!(Some($t::MAX), cast::<$f, $t>(max));
assert_eq!(None, cast::<$f, $t>(max + 1));
}
Equal => {
assert_eq!(Some($t::MAX), cast::<$f, $t>($f::MAX));
}
Less => {
assert_eq!(Some($f::MAX as $t), cast::<$f, $t>($f::MAX));
}
}
}
test_edge();
})+}
}
#[test]
fn cast_int_to_int_edge_cases() {
use core::cmp::Ordering::*;
macro_rules! test_edge {
($( $from:ident )+) => { $({
int_test_edge!($from -> { isize i8 i16 i32 i64 } with i64 u64);
int_test_edge!($from -> { usize u8 u16 u32 u64 } with i64 u64);
})+}
}
test_edge!(isize i8 i16 i32 i64);
test_edge!(usize u8 u16 u32 u64);
}
#[test]
#[cfg(has_i128)]
fn cast_int_to_128_edge_cases() {
use core::cmp::Ordering::*;
macro_rules! test_edge {
($( $t:ident )+) => {
$(
int_test_edge!($t -> { i128 u128 } with i128 u128);
)+
int_test_edge!(i128 -> { $( $t )+ } with i128 u128);
int_test_edge!(u128 -> { $( $t )+ } with i128 u128);
}
}
test_edge!(isize i8 i16 i32 i64 i128);
test_edge!(usize u8 u16 u32 u64 u128);
}
#[test]
fn newtype_from_primitive() {
#[derive(PartialEq, Debug)]
struct New<T>(T);
// minimal impl
impl<T: FromPrimitive> FromPrimitive for New<T> {
fn from_i64(n: i64) -> Option<Self> {
T::from_i64(n).map(New)
}
fn from_u64(n: u64) -> Option<Self> {
T::from_u64(n).map(New)
}
}
macro_rules! assert_eq_from {
($( $from:ident )+) => {$(
assert_eq!(T::$from(Bounded::min_value()).map(New),
New::<T>::$from(Bounded::min_value()));
assert_eq!(T::$from(Bounded::max_value()).map(New),
New::<T>::$from(Bounded::max_value()));
)+}
}
fn check<T: PartialEq + Debug + FromPrimitive>() {
assert_eq_from!(from_i8 from_i16 from_i32 from_i64 from_isize);
assert_eq_from!(from_u8 from_u16 from_u32 from_u64 from_usize);
assert_eq_from!(from_f32 from_f64);
}
macro_rules! check {
($( $ty:ty )+) => {$( check::<$ty>(); )+}
}
check!(i8 i16 i32 i64 isize);
check!(u8 u16 u32 u64 usize);
}
#[test]
fn newtype_to_primitive() {
#[derive(PartialEq, Debug)]
struct New<T>(T);
// minimal impl
impl<T: ToPrimitive> ToPrimitive for New<T> {
fn to_i64(&self) -> Option<i64> {
self.0.to_i64()
}
fn to_u64(&self) -> Option<u64> {
self.0.to_u64()
}
}
macro_rules! assert_eq_to {
($( $to:ident )+) => {$(
assert_eq!(T::$to(&Bounded::min_value()),
New::<T>::$to(&New(Bounded::min_value())));
assert_eq!(T::$to(&Bounded::max_value()),
New::<T>::$to(&New(Bounded::max_value())));
)+}
}
fn check<T: PartialEq + Debug + Bounded + ToPrimitive>() {
assert_eq_to!(to_i8 to_i16 to_i32 to_i64 to_isize);
assert_eq_to!(to_u8 to_u16 to_u32 to_u64 to_usize);
assert_eq_to!(to_f32 to_f64);
}
macro_rules! check {
($( $ty:ty )+) => {$( check::<$ty>(); )+}
}
check!(i8 i16 i32 i64 isize);
check!(u8 u16 u32 u64 usize);
}