On LogicBlox 4.0, numerics have been simplified by introducing the types “int”, “float”, and “decimal” without explicit bit widths, introducing a new syntax for literal constants, and removing implicit type conversions between numerics.

Porting code from LogicBlox 3.x to LogicBlox 4.0 is a straightforward source code transformation that can be aided by a script for rewrites, as well as compiler-generated warnings and errors. The old numeric types with explicit bit widths are deprecated but still supported in 4.0. We strongly recommend porting existing code because types in services/protobufs, literals, and explicit type conversions will have to change.

The main caveat is that **we recommend changing code from using binary floating-point ‘float’ numbers to decimal fixed-point ‘decimal’ numbers**, because the latter offer more accuracy, efficiency, and safety especially for financial applications.

**TYPES**

Numeric types have changed as follows.

The new **type ‘int’** for integers replaces the old unsigned and signed integer types with explicit bit widths (‘uint[8]’, ‘uint[16]’, ‘uint[32]’, ‘uint[64]’, ‘int[8]’, ‘int[16]’, ‘int[32]’, ‘int[64]’). Integers are implemented as signed 64-bit numbers.

The new **type ‘float’** binary floating-point replaces the old float types with explicit bit widths (‘float[32]’, float[64]’). Floats are implemented as 64-bit IEEE 754 binary floating-point numbers and are preferable for scientific applications. The LogicBlox 4.0 type float has the same size and precision as ‘float[64]’ in LogicBlox 3.x and ‘double’ in most languages that implement both double and float. Floats can represent a wider range of values and provide more precise multiplications and divisions than decimal fixed-point numbers, but they have more severe round-off errors which can lead to unintuitive behavior.

The new **type ‘decimal’** for decimal fixed-point numbers replaces the old decimal types with explicit bit widths (‘decimal[64]’, ‘decimal[128]’). Decimals are implemented with an integer part of up to 13 digits and a fractional part of up to 5 digits. Decimal numbers have better behavior over floats, which leads to improved accuracy, efficiency, and safety over floats. Decimal additions and subtractions are exact, and thus are preferable for total-aggregations, e.g., of money; for comparison floating-point addition is not associative, which makes aggregations require expensive data structures and still yields inexact answers. Decimal arithmetic checks for overflow and aborts a transaction, as opposed to silent floating-point overflows that can be hard to detect.

**LITERALS**

Numeric literals have changed with the introduction of an explicit suffix syntax to distinguish decimal and float literals; and literals without the explicit suffix are parsed as decimals instead of floats.

Integer literals, such as ‘1’, remain the same.

**Float literals** must have an **explicit ‘f’ suffix**. For example:

- ‘1.2f’ instead of ‘1.2’,
- ‘3E4f’ instead of ‘3E4’,
- ‘5.6E7f’ instead of ‘5.6E7’.

**Decimal literals** can have an **optional ‘d’ suffix**. For example, ‘1.2’ or ‘1.2d’ on LogicBlox 4.0, instead of ‘string:decimal64:convert[“1.2”]’ on LogicBlox 3.x. Note that there is no exponent notation, such as ’3E4′, for decimals.

Note that, in particular, **a literal ‘1.2’ is interpreted as a decimal on ****LogicBlox 4.0, instead of as a float on LogicBlox 3.x**. As such, the 4.0 compiler will give a type error on programs that use the ‘1.2’ literal where a floating-point number is expected. We have introduced this change to encourage applications to migrate uses of the ‘float[32]’ and ‘float[64]’ types on 3.x to the ‘decimal’ type on 4.0, while the literal remains ‘1.2’. If floating-point numbers are still preferred, then the type should become ‘float’ and the literal should become ‘1.2f’ with the explicit ‘f’ suffix.

**STANDARD LIBRARY FUNCTIONS**

Standard library functions for integers use an **‘int:’ path** instead of ‘uint8:’, ‘int8:’, etc.; for binary floating-point numbers use a **‘float:’ path **instead of ‘float32:’ or ‘float64:’; and for decimal fixed-point numbers use a **‘decimal:’ path** instead of ‘decimal64:’ or ‘decimal128:’. For example,

- ‘int:add[i1,i2]=i3’ instead of ‘int32:add[i1,i2]=i3’, and
- ‘float:decimal:convert[f]=d’ instead of ‘float64:decimal64:convert[i]=f’.

Conversions between integers, between floats, or between decimals become unnecessary. For example:

- ‘u=i’ instead of ‘uint32:int16:convert[u]=i’ or ‘uint32:int16:eq[u]=i’,
- ‘f=g’ instead of ‘float32:float64:convert[f]=g’ or ‘float32:float64:eq[f]=g’, and
- ‘d=e’ instead of ‘decimal64:decimal128:convert[d]=e’.

**NUMERIC TYPE CONVERSIONS**

On LogicBlox 3.x, numerics were implicitly converted from integer to float to decimal of various widths, based on heuristics guided by the types. For example, the comparison ‘1 < 3.14’ of an integer ‘1’ and float ‘3.14’ would be interpreted as ‘float32:lt_2(int32:float32:eq[1], 3.14)’, where ‘1’ is converted from ‘int[32]’ to ‘float[32]’, and the comparison ‘float32:lt_2’ is performed on ‘float[32]’ arguments.

Since the conversions between integer, float, and decimal types are lossy (a value in one type may not be representable in the others), inserting implicit conversions led to subtle behaviors.

**On LogicBlox 4.0, the compiler no longer introduces implicit conversions between numerics. **Therefore, **source programs must include explicit conversions** as appropriate.

For example, the ‘1 < 3.14’ comparison can be ported to LogicBlox 4.0 in several ways. **The recommended and most straightforward translation is ‘1.0 < 3.14’ ****where both literals are now decimals**; this is equivalent to ‘1.0d < 3.14d’ and ‘decimal:lt_2(1.0, 3.14)’. Alternatively, the literal ‘1’ can be explicitly converted from integer to decimal as: ‘string:decimal:convert[int:string:convert[1]] < 3.14’. At present, LogicBlox 4.0 doesn’t have ‘int:decimal:eq, but the conversion can be achieved by using ‘int:string:convert[i]=s’ to convert to a string, and ‘string:decimal:convert[s]=d’ to convert to a decimal. The ‘int:decimal:convert’ function will be added in the near future.

If floats are still preferred, the ‘1 < 3.14’ comparison can be ported as ‘1.0f < 3.14f’ by making the integer ‘1’ a float ‘1.0f’ with the explicit ‘f’ suffix, or as ‘int:float:convert[1] < 3.14f’ with an explicit ‘int:float:convert’ conversion from ‘int’ to ‘float’.

**LIMITED BACKWARDS COMPATIBILITY in 4.0**

For backwards compatibility during the transition period, the old form of types and standard library functions (e.g., ‘int[32]’ and ‘int32:add’) are be interpreted by the compiler by omitting the bit width precision (e.g., as ‘int’ and ‘int:add’). The compiler will emit a deprecation warning and this support will be discontinued in LogicBlox 4.2. Therefore, platform users are encouraged to migrate their code to use the new numeric forms.

**TEXT REPLACEMENT**

To aid in migration, we provide the following **sed rewrite scripts**. Each rewrite has the form ‘/oldcode/newcode/’ where ‘oldcode’ is a regular expression for old types, paths, or literals;’newcode’ is the updated type or path without bit widths, or the updated literal with a disambiguating suffix. Several scripts can be applied to a file ‘oldfile.logic’ to produce an updated file ‘newfile.logic’ by running:

sed -e “s/oldcode1/newcode1/g” -e “s/oldcode2/newcode2/g” … oldfile.logic > newfile.logic

These scripts have been tested to port code to LogicBlox 4.0 and there are several known caveats. The rewrites do not insert explicit type conversions between numerics, and they will replace text in comments and string literals which may be unintended. Users are advised to apply the rewrites and verify that there are no unintended changes between ‘oldfile.logic’ and ‘newfile.logic’, then try recompiling the latter to determine where type conversions may be needed.

**Integer** types (e.g., ‘int[64]’) and standard library paths (e.g., ‘int64’) can be replaced by ‘int’ with:

/\([^a-z]\)\(u\|\)int\[\(8\|16\|32\|64\)\]/\1int/

/\([^a-z]\)\(u\|\)int\(8\|16\|32\|64\)/\1int/

**Float** types (e.g., float[64]’) and standard library paths (e.g., ‘float64’) can be replaced by ‘float’, and float literals can be replaced with a suffix ‘f’. Please remember that it may be preferable to change floats to decimals for accuracy, efficiency, and safety.

/\([^a-z]\)float\[\(32\|64\)\]/\1float/

/\([^a-z]\)float\(32\|64\)/\1float/

/\([0-9]+\(\.[0-9]+[eE][\+-]?[0-9]+\|\.[0-9]+\|[eE][\+-]?[0-9]+\)\)\([^0-9eEf\”]\)/\1f\3/

**Decimal** types (e.g., ‘decimal[64]’) and standard library paths (e.g., ‘decimal64’) can be replaced by ‘decimal’ with:

/\([^a-z]\)decimal\[\(64\|128\)\]/\1decimal/

/\([^a-z]\)decimal\(64\|128\)/\1decimal/

Replacing types and paths from **float to decimal **can be done as follows (but shouldn’t be combined with the above float-to-float rewrite):

/\([^a-z]\)float\[\(32\|64\)\]/\1decimal/

/\([^a-z]\)float\(32\|64\)/\1decimal/

Thanks for the detailed post! I wish we had this before migrating bloxweb code to the new numerics 😉

Sorry this wasn’t available sooner. It was somewhat prompted by questions relating to that migration. 🙂

Are there changes to the types that are supported in protocol buffer? For instance, can I still use the primitive type int32 in a protocol message specification?

Thanks, great question! We’ll only use protobuf int64s for LB ints, and protobuf (64-bit) doubles for LB floats. In some cases, LB floats and decimals are represented by protobuf strings.

Put another way, smaller protobuf types such as int32, uint64, and (32-bit) float are incompatible with the LogicBlox 4.0 compiler.