#numbers #variables #integer #overflow #operations #primitive #length

var_num

Variable length number implementation that can be used as a drop in replacement for any number primitive

12 unstable releases (3 breaking)

0.4.2 Jul 17, 2024
0.4.1 Jul 15, 2024
0.3.0 Jul 15, 2024
0.2.4 Jul 13, 2024
0.1.2 Jul 10, 2024

#646 in Algorithms

Download history 140/week @ 2024-06-29 232/week @ 2024-07-06 589/week @ 2024-07-13 30/week @ 2024-07-20 122/week @ 2024-07-27

535 downloads per month
Used in 3 crates (2 directly)

MIT license

51KB
1.5K SLoC

var_num

The idea from this crate was inspired by Rob Pike' - "What We Got Right, What We Got Wrong" talk where he mentions that the int type should've been variable sized, "what if they didn't overflow". This crate was build to be used by rigz_vm/fn_vm, to simplify dealing with numbers.

Variable length number implementation that can be used as a drop in replacement for any number primitive, values are stored in their smallest representation initially (when calling From). For mathematical operations the output value matches the overall type of the left hand side. Assume the following are converted to values first:

i32::MAX - i16::MAX = i16
f32::MAX + u8::MAX = f32 // overflow, TODO #1

Components

The following enums should not be created directly, instead use the From trait to convert to the desired type.

  • VarNum: The main type, used to store numbers and represent any number.
  • VarFloat: A floating point type that can be used to store any floating point number.
  • VarInt: A signed integer type that can be used to store any integer, supports overflow and underflow automatically.
  • VarUInt: An unsigned integer type that can be used to store any unsigned integer.

Usage

use var_num::VarNum;

fn main() {
    let a: u8 = 255;
    let b: f32 = 3.14;
    let a: VarNum = a.into();
    let b: VarNum = b.into();
    let num = a + b;
    // num is a VarNum::U16(258)
    let num = b + a;
    // num is a VarNum::F32(258.14)
}

Special Cases

Float and UInt are left unchanged, but they include all operations available on ints. For ints this crate used checked_* to handle overflow or underflow with a few short circuits to skip the operation.

The BitAnd, BitOr, BitXor, Shl, and Shr operators are unchanged and will not change the size of the value. With the first three operators the size of the value can't change, and there were no other special cases; for the last two I personally don't want my numbers changing size when I shift them (even ints, this may change in a future version).

TODO

  • Automatically scale up from f32 to f64 when needed, implement checked_* for Float.
  • allow scaling up UInt through scaled_* functions, not default behavior
  • Allow explicit scaling for Shl or Shr, scaled_shl & scaled_shr
  • Add support for nightly custom types through feature
  • Benchmarking (From, operations, to/from bytes, etc.)

Dependencies

~1.2–1.9MB
~33K SLoC