Numeric Types

Integers

Universal Binary JSON (UBJSON) defines 5 types of increasing size for both signed and unsigned integer values.

Signed Values

'i' is used to mark the start of a signed integer value. The marker is then followed by the number of bytes used to store the value of this integer. Lastly, the actual bytes used to represent the signed integer value in little-endian order.

Format

[i][1, 2, 4, 8 or 16][value]
TypeMarker(s)SizeValue Range
int8[i][1]
or
01101001 00000001
3 bytes[-27, 27-1]
int16[i][2]
or
01101001 00000010
4 bytes[-215, 215-1]
int32[i][4]
or
01101001 00000100
6 bytes[-231, 231-1]
int64[i][8] and decimal value 8
or
01101001 00001000
10 bytes[-263, 263-1]
int128[i][16] and decimal value 16
or
01101001 00010000
18 bytes[-2127, 2127-1]

Encoding

All integer types (signed and unsigned) follow the format of:

 1 byte            1 byte                             1-16 bytes
[------][--------------------------------][-------------------------------]
[marker][value length of 1, 2, 4, 8 or 16][value as bytes in little endian]

Examples

(int8)   [i][1][-4]
(int16)  [i][2][10000]
(int32)  [i][4][-1347131688]
(int64)  [i][8][4111326098433108997]
(int128) [i][16][-132761764784733316829473709120341]

Binary Format

    'i'     size    value
|--------|--------|--------|--------|--------|--------|--------|--------|
 (int8)
 01101001 00000001 11111100

 (int16)
 01101001 00000010 00010000 00100111

 (int32)
 01101001 00000100 11011000 01100110 10110100 10101111
 
 (int64)
 01101001 00001000 00000101 11101100 11100101 11000100 01001101 01011101
 00001110 00111001
 
 (int128)
 01101001 00010000 00000000 10011100 00011111 00000011 10100000 11011001 
 11001111 01010001 00001111 00100111 01100101 10111101 00001111 00100010 
 01101111 11110101 11011100

Example Files

Unsigned Values

'u' is used to mark the start of an unsigned integer value. The marker is then followed by the number of bytes used to store the value of this integer. Lastly, the actual bytes used to represent the unsigned integer value in little-endian order.

Format

[u][1, 2, 4, 8 or 16][value]
TypeMarker(s)SizeValue Range
uint8[u][1]
or
01110101 00000001
3 bytes[0, 28-1]
uint16[u][2]
or
01110101 00000010
4 bytes[0, 216-1]
uint32[u][4]
or
01110101 00000100
6 bytes[0, 232-1]
uint64[u][8]
or
01110101 00001000
10 bytes[0, 264-1]
uint128[u][16]
or
01110101 00010000
18 bytes[0, 2128-1]

Encoding

All integer types (signed and unsigned) follow the format of:

 1 byte            1 byte                             1-16 bytes
[------][--------------------------------][-------------------------------]
[marker][value length of 1, 2, 4, 8 or 16][value as bytes in little endian]

Examples

(uint8)   [u][1][225]
(uint16)  [u][2][57558]
(uint32)  [u][4][3347131688]
(uint64)  [u][8][14446744073709551615]
(uint128) [u][16][207520602136205146633900898310899567]

Binary Format

    'u'     size    value
|--------|--------|--------|--------|--------|--------|--------|--------|
 (uint8)
 01110101 00000001 11100001

 (uint16)
 01110101 00000010 11010110 11100000

 (uint32)
 01110101 00000100 00101000 00101101 10000001 11000111
 
 (uint64)
 01110101 00001000 11111111 11111111 01101111 01100010 00110001 00100101
 01111101 11001000
 
 (uint128)
 01110101 00010000 

Example Files

Floating Point

All representations of floating-point numbers in this specification follow the IEEE 754-2008 revision (PDF).

Format

[f][1, 2, 4, 8 or 16][value]
TypeMarker(s)SizeValue Range
float16[f][2]
or
01101001 00000010
4 bytesbinary16 (IEEE 745-2008)
float32[f][4]
or
01101001 00000100
6 bytesbinary32 (IEEE 745-2008)
float64[f][8]
or
01101001 00001000
10 bytesbinary64 (IEEE 745-2008)
float128[f][16]
or
01101001 00010000
18 bytesbinary128 (IEEE 745-2008)

Encoding

All floating point types follow the format of:

 1 byte            1 byte                             1-16 bytes
[------][--------------------------------][-------------------------------]
[marker][value length of 1, 2, 4, 8 or 16][value as bytes in little endian]

Binary encoding of floating-point values follow the IEEE 754-2008 standard and are stored in little endian order.

float16

  fraction    exp   s
[##########][#####][#]

bit 0-9   (10 bits) significand/mantissa/fraction
bit 10-14 (5 bits)  exponent
bit 15    (1 bit)   sign (0 positive, 1 negative)

float32

         fraction           exp     s
[#######################][########][#]

bit 0-22   (23 bits) significand/mantissa/fraction
bit 23-30  (8 bits)  exponent
bit 31     (1 bit)   sign (0 positive, 1 negative)

float64

                      fraction                             exp      s
[####################################################][###########][#]

bit 0-51   (52 bits) significand/mantissa/fraction
bit 52-62  (11 bits) exponent
bit 63     (1 bit)   sign (0 positive, 1 negative)

float128

                                               fraction                                                                  exp        s
[################################################################################################################][###############][#]

bit 0-111   (112 bits) significand/mantissa/fraction
bit 112-126 (15 bits)  exponent
bit 127     (1 bit)    sign (0 positive, 1 negative)

Examples

(float16)   [f][16][1.23] '1.23'
(float16)   [f][16][-5.2] '-5.2'
(float32)   [f][32][171.328] '171.328'
(float32)   [f][32][-1428.99156] '-1428.99156'
(float64)   [f][64][3.141592653589793] '3.141592653589793'
(float64)   [f][64][-3.141592653589793] '-3.141592653589793'
(float128)  [f][128][a big decimal number] <a big decimal number>

Binary Format

TBD...

Unlimited Precision Number

A less performant, but infinitely portable encoding mechanism by which arbitrarily large, small, positive or negative floating point or integer numbers can be encoded as a string and easily passed between (or through) systems without concern for the values being corrupted or mishandled during processing or transport.

Guideline

Using this numeric type makes sense in two cases:

  1. Dealing in values larger than 128-bit
  2. Passing values around and through intermediary systems that do not have native/primitive number type support large enough to handle the values without corruption them (or causing an exception).

Most modern platforms, as of 2020, support a native 64-bit numeric type, but going bigger than that it quickly becomes a problem.

If your application commonly deals in numeric values that large and you are transferring data across system boundaries you don’t control, using this number type might be the most compatible way to proceed.