Skip to content

0.10.0

Compare
Choose a tag to compare
@maciejhirsz maciejhirsz released this 22 Jul 22:36
· 91 commits to master since this release

Breaking changes

  • JsonValue::Number now stores a new json::number::Number type instead of f64.
  • Error::UndefinedField was removed since it wasn't used anywhere.

The new Number

This release introduces a new Number type. The RFC 7159 talks about the interoperability issues between JSON implementations and allows them to limit the precision range. The recommended minimum is double precision IEEE 754 binary floating points as the base number type, as support for it is widely available.

The new Number type is composed of a 64 bit unsigned mantissa, 16 bit signed decimal exponent and a marker for a sign or NaN-ness of the number. It's thus capable of representing the full range of u64 and i64 integers, as well as the full range of f64, including negative zero. Infinite values were omitted as they cannot be represented in JSON.

While Rust lacks a native decimal floating point type, json-rust provides fast conversion from any Rust number type to the new Number, and by extension to JsonValue. All methods for obtaining numbers as specific types work just as well as they did before, and the as_u64 / as_i64 methods can now produce precise values.

Fixed point arithmetic

Two new methods have been added to the JsonValue type:

  • as_fixed_point_u64
  • as_fixed_point_i64

Those make it easy to work with numeric values where a fixed amount of fractional digits is expected. For example: a price such as 4.99 can be obtained as an integer of cents 499 using .as_fixed_point_u64(2), where 2 is the number of digits in the fraction. This conversion is very cheap and (since it works on decimal exponents) free of any rounding errors that plague binary floating points.

Upgrading

If you have been using JsonValue::Number directly to construct numeric values, the easiest way to upgrade to 0.10 is to replace expressions such as JsonValue::Number(10) with JsonValue::from(10). The From and Into traits implemented for JsonValue should always keep you covered with the best performance.

Performance

Since decimal floats are represented in a form that closely matches the e notation in JSON strings, printing them out to strings is very fast and always accurate. In benchmarks testing stringifying of large amount of floating point numbers, json-rust is now nearly 3 times faster than the previous release.

This performance gain is, however, a tradeoff. If you create numbers by conversion from f64 (for example: JsonValue::from(3.14)) you will pay part of the cost that would previously be attached to stringifying head on. The algorithm used for that is the same as we've used before for writing f64 to strings, and the combined cost of conversion to decimal float + printing is virtually identical to that of printing a f64 itself. Converting from decimal floating point to f64 is still very cheap, the cost of reading parsed numbers hasn't changed in practice.

In future releases I plan to work on procedural macros, so that writing 3.14 inside array! or object! will compile to a Number construct directly, without converting from f64 on runtime, thus mostly eliminating the cost of the tradeoff.