Precision of floating numbers

Precision of floating numbers is limited, detailed description can be found in the AS Help.
REAL: B&R Online Help
LREAL: B&R Online Help

There are several sources in the web explaining reason for limited precision of floting numbers in detail (google for “float precision”). Already 0.01 e.g. is only representable with limited precision with datatype REAL.

Handling on PLC:

  1. Be aware of rounding errors in your calculations

  2. Don’t do comparison (IF SomeREAL = 0.1 THEN …) of floating numbers

  3. Differences C vs. ST: C uses per default double/LREAL (8 byte) precision while ST uses per default float/REAL (4 byte) precision.

Sample for saving 0.01 into a LREAL variable SomeLReal_C / SomeLReal_ST:

SomeLReal_C = 0.01; // (8 byte precision)  Result = 0.01
SomeLReal_C = 0.01F; // (4 byte precision)  Result = 0.00999999...
SomeLReal_ST := 0.01; // (4 byte precision) Result = 0.00999999...
SomeLReal_ST := LREAL#0.01; // (8 byte precision) Result = 0.01

Use-cases/Samples:

  1. Saving values into a XML file using MpRecipe leads to “wrong” representation:
LREAL := 660.4; // => final value in the XML file = 660.39999999999998

=> This is normal behaviour, caused by limited precision of LREAL / DOUBLE (8 byte).

  1. Library AsBrStr, brsatof is used to convert STRING into REAL, but the FUB does not correctly convert the value:
brsstrcpy((UDINT) SomeString , (UDINT) "23.8338570");
SomeReal = brsatof((UDINT) SomeString );
// => Result: SomeReal = 23.8338566;

=> This is again normal behaviour, caused by limited precision of REAL / FLOAT (4 byte).

Alternatives:
In case the limited precision is a problem, an alternative is to use integer datatypes.

So e.g. for use-case 1:
DINT = 6604
(Of course information about precision / number of decimal places is needed in this case for further processing)

2 Likes

This is great information to have out in the community. I use a website to demonstrate the precision of IEEE 754 to myself and others.
Link: IEEE-754 Floating Point Converter

The basic example is to enter 0.2 and see the error due to how the floating point must be stored in base-2 computing.

Added bonus is the hexadecimal representation, which helps if you are looking at unformatted data (such as coming across a communication bus) to find the floating point value.

1 Like

What is the correct way of checking if a floating point value can cause a division by zero error in B&R?

Will something like A / B always work if B <> 0.0? Or do I need to check if ABS(B) > 0.000001 for example?

What is the correct way of checking if a floating point value can cause a division by zero error in B&R?

It depends on if you only want divide by zero error protection or want reasonable limits as well. Only true 0.0 will produce a fatal error, but 1.0 / 1.17E-38 isn’t likely to yield a usable result.

I’ve never seen a fatal error because of a floating-point division. How / where does it happen? Does it need a specific CPU?

Floating point divisions by 0.0 are possible and the results are defined in the IEEE format. Floating point supports:

  • Negative infinity (-inf)
  • Positive infinity (inf)
  • Not a number (NaN)
OneDivZero := 1.0 / 0.0;
NegOneDivZero := -1.0 / 0.0;
ZeroDivZero := 0.0 / 0.0;

image

1 Like

I’ve never seen a fatal error because of a floating-point division. How / where does it happen? Does it need a specific CPU?

I would have said it’s not CPU specific and is floating-point universal, but it’s been a long while since I caused one :sweat_smile:. The errors I am thinking of are 9104/25300 and are critical exceptions (reboot into SERVICE mode). See B&R Online Help - 9104 (br-automation.com), B&R Online Help - 25300(br-automation.com). You can also see (possibly vestigial) functions in the IECChk library.

However, this information appears to be outdated, or at the very least, incomplete. Testing in both ARSim and AREmb (AR 4.83 - Intel Atom CPU) results in the positive/negative/NaN indicated by Patrick.

+1 Orange pt, Patrick.

The errors mentioned there do still exist and these are for integers. For integers they make sense, as there is no way to display these special values in integer representation. So, the info is not outdated. However, Arm processors don’t even have these exceptions for integers and just return 0.

Yes, I stumbled on that once, when I made a GetNan() function in ST, as there is no constant or function existing in the IEC system. Obviously the IecCheck did’t like

FUNCTION GetNan
    GetNan := 0.0 / 0.0;
END_FUNCTION

For some users the division check for REAL might be ok, but dividing by 0 can be a real use case.