Manipulating real numbers

This topic depicts issues, related to using real numbers, while manipulating FIX messages, as well as solutions for the issues, are offered by the FIX Engine.

FIX Protocol supposes FIX messages that exchange using 'tag=value' presentation. Except special cases, field value is a text-based, so regular presentation of real numbers with decimal point is used:

35=x|268=3|...|**270=9462.50**|271=5

Interface of the OnixS::FIX::Message class that encapsulates primary operations over FIX fields for a FIX message, offers a few overloads, allowing users to setup field values of native language types like int, long and double. The field value setup function-member converts the value of a primitive type into its string presentation using natural transformation.

However, for native types like float and double, there's no unambiguous transformation available. That's because double values usually have no finite presentation due to the limitation of the data type.

In particular, assume the following initialization of a variable of double type is executed:

const double Price = 100.10;

In spite of exact constant definition, the real value, hold by a defined variable, will look like **100.099999999999998**, but not **100.1**.

This is because each double value is expressed as **X * 2 ^ Y**, where **X** is mantissa and **Y** is 2-basis exponent.

As a result, each time value of a double type is assigned as a FIX field value, this issue might happen when transformation into string gives different text presentation:

35=x|...|**270=9462.499999999998**|

in compare to the expected one:

35=x|...|**270=9462.50**|

As a common solution for this case, OnixS::FIX::FieldSet::set and OnixS::FIX::FieldSet::setV overloads expose additional parameter which defines rounding precision for a double to string transformation.

As a result, transformation rounds value is being set to the given number of digits after the decimal point:

order.set(Tags::Price, 9462.50, 2);

Using rounding precision usually avoids the appearance of infinite approximated values in FIX messages, because the being set value is rounded ("rounding to nearest" is used.) to a strictly defined precision. Also, please note that the default precision value is 17 digits, so you can get the "Cannot transform Double value (..) into decimal" exception, during setting, if the value is too big for such precision. When the FIX Engine manipulates double values, it transforms these values into the internal decimal representation. This decimal type uses the 64-bit integer mantissa and 32-bit integer exponent to store decimal values. Therefore, e.g. the value 420.0 with the 17 digits precision after the decimal point cannot fit in 64-bit integer mantissa (digits before the decimal point are included also), so the mentioned exception is thrown. In such cases, you can change the precision to a lower number, e.g. 10, then the value can fit in 64-bit integer mantissa and the exception will not be thrown.

In accordance with the FIX specification, trailing zeros in decimal string presentation make no sense. In other words, the following strings are the same: "23.0" = "23.0000" = "23". Therefore, OnixS::FIX::Message class discards trailing zeros in the string presentation of decimal values. However, there may be some venues, which require trailing zeros for decimal values of some fields (e.g. Price <44> field). In such cases, you can use the OnixS::FIX::FieldSet::set method for string values and set such decimal values as string values:

order.set(Tags::Price, "1.29900");

Additionally, you can use e.g. std::sprintf function and assign the field value as a pre-formatted string rather than a double value:

double price = 1.2975;

char preformattedPrice[100];

//Set 5 digits after decimal point.

std::sprintf(preformattedPrice,"%.5f", price);

//Set the price value as string with trailing zeros if necessary.

//String presentation of price will be "1.29750".

order.set(Tags::Price, preformattedPrice);

Since the version 3.0 FIX Engine exposes OnixS::FIX::Decimal class for better operability over real numbers like prices and quantities are.

In contrast to a double type, OnixS::FIX::Decimal presents real numbers use 10-basis exponent. Therefore, each number looks like **X * 10 ^ Y**, where **X** is mantissa and **Y** is an exponent.

Also, being C++ class, OnixS::FIX::Decimal offers to construct from 64-bit integer mantissa and 32-bit integer exponent. In this case, it eliminates unambiguity and approximation during the defining value:

// Defines 12.34 value.

const Decimal Price(1234, -2);

Parsing OnixS::FIX::Decimal from the text presentation is also free of approximation issues in bounds of values, operated in FIX messages.

- Note
- Manipulating prices and quantities as values of OnixS::FIX::Decimal type also has performance benefits. In particular, OnixS implementation of string to OnixS::FIX::Decimal, and vice versa conversion, is often a few times faster than string to double and double to string transformation, exposed by standard C/C++ runtime library.

Thus, operating prices and quantities as OnixS::FIX::Decimal, instead of double types, eliminates issues that are caused by an inexact value presentation, as well as gives performance benefits.