Repository: maciejhirsz/json-rust (rust)

Issue:
Panic: attempt to subtract with overflow
```rust
extern crate json;
use json::number::Number;

fn main() {
    let n = Number::from_parts(false, 10000000000000000000, -18);
    println!("{}", n);
}
```

```
thread 'main' panicked at 'attempt to subtract with overflow', json-0.11.5/src/util/print_dec.rs:160
```

Pull request description:
Issue 108
Fixes #108.

A separate logic for handling cases where the exponent is negative, less than -18, but the base number is big enough so that adjusting the exponent when moving the decimal point makes the exponent positive (or zero).

Patch:
diff --git a/src/util/print_dec.rs b/src/util/print_dec.rs
index 15a34bc..ea55aee 100644
--- a/src/util/print_dec.rs
+++ b/src/util/print_dec.rs
@@ -117,6 +117,11 @@ pub unsafe fn write<W: io::Write>(wr: &mut W, positive: bool, mut n: u64, expone
         }
 
         // Not easily printable, write down fraction, then full number, then exponent
+
+        // Since we move the decimal point right after the first digit, we have to adjust the
+        // exponent part. If the number is long enough, this may result in the exponent switching
+        // sign from negative to positive - we have to handle this case separately.
+        let mut exponent_positive = false;
         if n < 10 {
             // Single digit, no fraction
             curr -= 1;
@@ -154,16 +159,24 @@ pub unsafe fn write<W: io::Write>(wr: &mut W, positive: bool, mut n: u64, expone
                 ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
             }
 
-            // Substract the amount of digits printed in the fraction
-            // from the exponent that we still need to print using
-            // the `e` notation
-            e -= buf.len() as u16 - curr as u16;
+            let printed_so_far = buf.len() as u16 - curr as u16;
+
+
+            if printed_so_far <= e {
+                // Subtract the amount of digits printed in the fraction
+                // from the exponent that we still need to print using
+                // the `e` notation
+                e -= printed_so_far;
+            } else {
+                // Same as e = |e - printed_so_far|.
+                e = printed_so_far - e;
+                exponent_positive = true;
+            }
 
             curr -= 1;
             *buf_ptr.offset(curr) = b'.';
 
             write_num(&mut n, &mut curr, buf_ptr, lut_ptr);
-
         }
 
         // Write out the number with a fraction
@@ -174,8 +187,16 @@ pub unsafe fn write<W: io::Write>(wr: &mut W, positive: bool, mut n: u64, expone
             )
         ));
 
-        // Write the remaining `e` notation
-        try!(wr.write_all(b"e-"));
+        // Omit the 'e' notation for e == 0
+        if e == 0 {
+            return Ok(());
+        }
+        // Write the remaining `e` notation, with proper sign
+        if exponent_positive {
+            wr.write_all(b"e+")?;
+        } else {
+            wr.write_all(b"e-")?;
+        }
         return write(wr, true, e as u64, 0);
 
     }


Test patch:
diff --git a/tests/number.rs b/tests/number.rs
index 3cb0efb..b0b2949 100644
--- a/tests/number.rs
+++ b/tests/number.rs
@@ -126,9 +126,3 @@ fn as_fixed_point_i64() {
 fn convert_f64_precision() {
     assert_eq!(Number::from_parts(true, 4750000000000001, -18), 0.004750000000000001);
 }
-
-#[test]
-fn issue_107() {
-    let n = Number::from_parts(true, 1, -32768);
-    assert_eq!(format!("{}", n), "1e-32768");
-}
\ No newline at end of file
diff --git a/tests/print_dec.rs b/tests/print_dec.rs
new file mode 100644
index 0000000..ba1088b
--- /dev/null
+++ b/tests/print_dec.rs
@@ -0,0 +1,21 @@
+extern crate json;
+
+use json::number::Number;
+
+#[test]
+fn issue_107() {
+    let n = Number::from_parts(true, 1, -32768);
+    assert_eq!(format!("{}", n), "1e-32768");
+}
+
+#[test]
+fn issue_108_exponent_positive() {
+    let n = Number::from_parts(true, 10_000_000_000_000_000_001, -18);
+    assert_eq!(format!("{}", n), "1.0000000000000000001e+1");
+}
+
+#[test]
+fn issue_108_exponent_0() {
+    let n = Number::from_parts(true, 10_000_000_000_000_000_001, -19);
+    assert_eq!(format!("{}", n), "1.0000000000000000001");
+}