Skip to content

Commit 7b45cf4

Browse files
committed
Docs
1 parent 1d44385 commit 7b45cf4

File tree

3 files changed

+133
-32
lines changed

3 files changed

+133
-32
lines changed

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,8 @@ can even outperform parsing to structs.
125125
This crate implements the standard according to the [
126126
RFC 7159](https://tools.ietf.org/html/rfc7159) and
127127
[ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf)
128-
documents. It makes some compromises when it comes to number precision, but they are
129-
both within the specification and advised practices for best interoperability,
130-
specifically those discussed in the RFC.
128+
documents. For the best interoperability numbers are treated stored as 64bit precision
129+
mantissa with 16 bit decimal exponent for floating point representation.
131130

132131
## License
133132

src/number.rs

Lines changed: 103 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::num::FpCategory;
33
use util::grisu2;
44
use util::write::write;
55

6+
/// NaN value represented in `Number` type. NaN is equal to itself.
67
pub const NAN: Number = Number {
78
category: NAN_MASK,
89
mantissa: 0,
@@ -13,14 +14,45 @@ const NEGATIVE: u8 = 0;
1314
const POSITIVE: u8 = 1;
1415
const NAN_MASK: u8 = !1;
1516

17+
/// Number representation used inside `JsonValue`. You can easily convert
18+
/// the `Number` type into native Rust number types and back, or use the
19+
/// equality operator with another number type.
20+
///
21+
/// ```
22+
/// # use json::number::Number;
23+
/// let foo: Number = 3.14.into();
24+
/// let bar: f64 = foo.into();
25+
///
26+
/// assert_eq!(foo, 3.14);
27+
/// assert_eq!(bar, 3.14);
28+
/// ```
29+
///
30+
/// More often than not you will deal with `JsonValue::Number` variant that
31+
/// wraps around this type, instead of using the methods here directly.
1632
#[derive(Copy, Clone, Debug)]
1733
pub struct Number {
34+
// A byte describing the sign and NaN-ness of the number.
35+
// category == 0 -> negative sign
36+
// category == 1 -> positive sign
37+
// category > 1 -> NaN
1838
category: u8,
39+
40+
// Decimal exponent, analog to `e` notation in string form.
1941
exponent: i16,
42+
43+
// Integer base before sing and exponent applied.
2044
mantissa: u64,
2145
}
2246

2347
impl Number {
48+
/// Construct a new `Number` from parts. This can't create a NaN value.
49+
///
50+
/// ```
51+
/// # use json::number::Number;
52+
/// let pi = Number::from_parts(true, 3141592653589793, -15);
53+
///
54+
/// assert_eq!(pi, 3.141592653589793);
55+
/// ```
2456
#[inline]
2557
pub fn from_parts(positive: bool, mantissa: u64, exponent: i16) -> Self {
2658
Number {
@@ -30,6 +62,17 @@ impl Number {
3062
}
3163
}
3264

65+
/// Reverse to `from_parts` - obtain parts from an existing `Number`.
66+
///
67+
/// ```
68+
/// # use json::number::Number;
69+
/// let pi = Number::from(3.141592653589793);
70+
/// let (positive, mantissa, exponent) = pi.as_parts();
71+
///
72+
/// assert_eq!(positive, true);
73+
/// assert_eq!(mantissa, 3141592653589793);
74+
/// assert_eq!(exponent, -15);
75+
/// ```
3376
#[inline]
3477
pub fn as_parts(&self) -> (bool, u64, i16) {
3578
(self.category == POSITIVE, self.mantissa, self.exponent)
@@ -50,11 +93,28 @@ impl Number {
5093
self.category & NAN_MASK != 0
5194
}
5295

96+
/// Test if the number is NaN or has a zero value.
5397
#[inline]
5498
pub fn is_empty(&self) -> bool {
5599
self.mantissa == 0 || self.is_nan()
56100
}
57101

102+
/// Obtain an integer at a fixed decimal point. This is useful for
103+
/// converting monetary values and doing arithmetic on them without
104+
/// rounding errors introduced by floating point operations.
105+
///
106+
/// Will return `None` if `Number` is negative or a NaN.
107+
///
108+
/// ```
109+
/// # use json::number::Number;
110+
/// let price_a = Number::from(5.99);
111+
/// let price_b = Number::from(7);
112+
/// let price_c = Number::from(10.2);
113+
///
114+
/// assert_eq!(price_a.as_fixed_point_u64(2), Some(599));
115+
/// assert_eq!(price_b.as_fixed_point_u64(2), Some(700));
116+
/// assert_eq!(price_c.as_fixed_point_u64(2), Some(1020));
117+
/// ```
58118
pub fn as_fixed_point_u64(&self, point: u16) -> Option<u64> {
59119
if self.category != POSITIVE {
60120
return None;
@@ -71,6 +131,17 @@ impl Number {
71131
})
72132
}
73133

134+
/// Analog to `as_fixed_point_u64`, except returning a signed
135+
/// `i64`, properly handling negative numbers.
136+
///
137+
/// ```
138+
/// # use json::number::Number;
139+
/// let balance_a = Number::from(-1.49);
140+
/// let balance_b = Number::from(42);
141+
///
142+
/// assert_eq!(balance_a.as_fixed_point_i64(2), Some(-149));
143+
/// assert_eq!(balance_b.as_fixed_point_i64(2), Some(4200));
144+
/// ```
74145
pub fn as_fixed_point_i64(&self, point: u16) -> Option<i64> {
75146
if self.is_nan() {
76147
return None;
@@ -381,35 +452,38 @@ impl ops::Neg for Number {
381452
}
382453
}
383454

384-
impl ops::Mul for Number {
385-
type Output = Number;
386-
387-
#[inline]
388-
fn mul(self, other: Number) -> Number {
389-
// If either is a NaN, return a NaN
390-
if (self.category | other.category) & NAN_MASK != 0 {
391-
NAN
392-
} else {
393-
Number {
394-
// If both signs are the same, xoring will produce 0.
395-
// If they are different, xoring will produce 1.
396-
// Xor again with 1 to get a proper proper sign!
397-
// Xor all the things! ^ _ ^
398-
399-
category: self.category ^ other.category ^ POSITIVE,
400-
exponent: self.exponent + other.exponent,
401-
mantissa: self.mantissa * other.mantissa,
402-
}
403-
}
404-
}
405-
}
406-
407-
impl ops::MulAssign for Number {
408-
#[inline]
409-
fn mul_assign(&mut self, other: Number) {
410-
*self = *self * other;
411-
}
412-
}
455+
// Commented out for now - not doing math ops for 0.10.0
456+
// -----------------------------------------------------
457+
//
458+
// impl ops::Mul for Number {
459+
// type Output = Number;
460+
461+
// #[inline]
462+
// fn mul(self, other: Number) -> Number {
463+
// // If either is a NaN, return a NaN
464+
// if (self.category | other.category) & NAN_MASK != 0 {
465+
// NAN
466+
// } else {
467+
// Number {
468+
// // If both signs are the same, xoring will produce 0.
469+
// // If they are different, xoring will produce 1.
470+
// // Xor again with 1 to get a proper proper sign!
471+
// // Xor all the things! ^ _ ^
472+
473+
// category: self.category ^ other.category ^ POSITIVE,
474+
// exponent: self.exponent + other.exponent,
475+
// mantissa: self.mantissa * other.mantissa,
476+
// }
477+
// }
478+
// }
479+
// }
480+
481+
// impl ops::MulAssign for Number {
482+
// #[inline]
483+
// fn mul_assign(&mut self, other: Number) {
484+
// *self = *self * other;
485+
// }
486+
// }
413487

414488
#[inline]
415489
fn decimal_power(e: u16) -> u64 {

src/value.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,13 +195,41 @@ impl JsonValue {
195195
}
196196
}
197197

198+
/// Obtain an integer at a fixed decimal point. This is useful for
199+
/// converting monetary values and doing arithmetic on them without
200+
/// rounding errors introduced by floating point operations.
201+
///
202+
/// Will return `None` if `Number` called on a value that's not a number,
203+
/// or if the number is negative or a NaN.
204+
///
205+
/// ```
206+
/// # use json::JsonValue;
207+
/// let price_a = JsonValue::from(5.99);
208+
/// let price_b = JsonValue::from(7);
209+
/// let price_c = JsonValue::from(10.2);
210+
///
211+
/// assert_eq!(price_a.as_fixed_point_u64(2), Some(599));
212+
/// assert_eq!(price_b.as_fixed_point_u64(2), Some(700));
213+
/// assert_eq!(price_c.as_fixed_point_u64(2), Some(1020));
214+
/// ```
198215
pub fn as_fixed_point_u64(&self, point: u16) -> Option<u64> {
199216
match *self {
200217
JsonValue::Number(ref value) => value.as_fixed_point_u64(point),
201218
_ => None
202219
}
203220
}
204221

222+
/// Analog to `as_fixed_point_u64`, except returning a signed
223+
/// `i64`, properly handling negative numbers.
224+
///
225+
/// ```
226+
/// # use json::JsonValue;
227+
/// let balance_a = JsonValue::from(-1.49);
228+
/// let balance_b = JsonValue::from(42);
229+
///
230+
/// assert_eq!(balance_a.as_fixed_point_i64(2), Some(-149));
231+
/// assert_eq!(balance_b.as_fixed_point_i64(2), Some(4200));
232+
/// ```
205233
pub fn as_fixed_point_i64(&self, point: u16) -> Option<i64> {
206234
match *self {
207235
JsonValue::Number(ref value) => value.as_fixed_point_i64(point),

0 commit comments

Comments
 (0)