# Another floating point casualty: round/integer arithmetic functions

**URL:** <https://swi-prolog.discourse.group/t/another-floating-point-casualty-round-integer-arithmetic-functions/5159>\
**Category:** Predicate\
**Tags:** bug\
**Created:** [March 23, 2022, 9:14pm UTC](https://swi-prolog.discourse.group/t/another-floating-point-casualty-round-integer-arithmetic-functions/5159 "2022-03-23T21:14:30Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![ridgeworks](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/ridgeworks/32/886_2.png) [@ridgeworks](https://swi-prolog.discourse.group/u/ridgeworks)\
**Post date:** [March 23, 2022, 9:14pm UTC](https://swi-prolog.discourse.group/t/another-floating-point-casualty-round-integer-arithmetic-functions/5159/1 "2022-03-23T21:14:30Z")

</div>

SWIP Version 8.4.1:

```prolog
?- X is nexttoward(0.5,-inf), N is round(X).
X = 0.49999999999999994,
N = 1.

?- X is nexttoward(0.5,-inf), N is integer(X).
X = 0.49999999999999994,
N = 1.

?- X is nexttoward(-0.5,inf), N is round(X).
X = -0.49999999999999994,
N = -1.

```

Looking at `pl-arith.c` source, function `ar_integer`, the problem seems to lie here:

```prolog
	{ r->value.i = (int64_t)(n1->value.f + 0.5);
	  if ( r->value.i < 0 ) /* Why can this happen? */
	    r->value.i = PLMAXINT;
	} else
	{ r->value.i = (int64_t)(n1->value.f - 0.5);
	  if ( r->value.i > 0 )
	    r->value.i = PLMININT;
	}

```

If I execute the following bit of C code in an online service ([C Online Compiler & Interpreter - Replit](https://replit.com/languages/c)):

```prolog
    double xpos = nexttoward( 0.5,-1);
    double xneg = nexttoward(-0.5, 1);
    printf("xpos = %24.20e xpos+0.5 = %24.20e\n",xpos, xpos+0.5);
    printf("xneg = %24.20e xneg-0.5 = %24.20e\n",xneg, xneg-0.5);
    printf("xpos = %24.20e lround(xpos) = %li\n",xpos,lround(xpos));
    printf("xneg = %24.20e lround(xneg) = %li\n",xneg,lround(xneg));

```

it prints

```prolog
xpos = 4.99999999999999944489e-01 xpos+0.5 = 1.00000000000000000000e+00
xneg = -4.99999999999999944489e-01 xneg-0.5 = -1.00000000000000000000e+00
xpos = 4.99999999999999944489e-01 lround(xpos) = 0
xneg = -4.99999999999999944489e-01 lround(xneg) = 0

```

so it looks like an issue with the C level arithmetic. Function `lround` seems to get it right so a quick fix might be to use that instead adding/subtracting 0.5.

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 24, 2022, 10:06am UTC](https://swi-prolog.discourse.group/t/another-floating-point-casualty-round-integer-arithmetic-functions/5159/3 "2022-03-24T10:06:48Z")

</div>

> [@ridgeworks](#):
>
> Function `lround` seems to get it right so a quick fix might be to use that instead adding/subtracting 0.5.

Updated that. Just, `lround(9223372036854775807.0)` produces a large **negative** integer ☹ So, I had to use the same kludge as before, if rounding a positive float returns a negative integer, use PLMAXINT and similar for the negative side. Why is this the case? lround() is supposed to raise a float exception if it cannot do its work. Is this a bug?

Added some more tests.

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 24, 2022, 11:07am UTC](https://swi-prolog.discourse.group/t/another-floating-point-casualty-round-integer-arithmetic-functions/5159/5 "2022-03-24T11:07:16Z")

</div>

As the docs say, SWI-Prolog rounds _outwards_, as most languages seem to do. Possibly the ISO flag should change this. GNU-Prolog (1.4.5) is rather strange:

```prolog
?- A is round(0.5).
A = 0
?- A is round(-0.5).
A = 0
?- A is round(1.5).
A is 2.

```

The first seems a bit strange to me as binary floats can represent 0.5 precisely, no? ECLiPSe returns round(0.5) as 0.0, which may give a clue why this happens?

---

<div class="post-metadata">

**Author:** ![Olopierpa](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/olopierpa/32/6855_2.png) [@Olopierpa](https://swi-prolog.discourse.group/u/Olopierpa)\
**Post date:** [March 24, 2022, 11:22am UTC](https://swi-prolog.discourse.group/t/another-floating-point-casualty-round-integer-arithmetic-functions/5159/7 "2022-03-24T11:22:32Z")

</div>

Looks like GNU-Prolog is rounding to even, which I thought is the most common default rounding mode.

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 24, 2022, 12:32pm UTC](https://swi-prolog.discourse.group/t/another-floating-point-casualty-round-integer-arithmetic-functions/5159/8 "2022-03-24T12:32:02Z")

</div>

> [@Olopierpa](#):
>
> GNU-Prolog is rounding to even

The code says it does the equivalent of `(long)rint(val)`. Linux docs for rint() say:

> … using the current rounding direction (see fesetround(3)) and without raising the inexact exception. When the current rounding direction is to nearest, these functions round halfway cases to the even integer in accordance with IEEE-754.

It seems there are many opinions 🙂

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 24, 2022, 1:59pm UTC](https://swi-prolog.discourse.group/t/another-floating-point-casualty-round-integer-arithmetic-functions/5159/9 "2022-03-24T13:59:32Z")

</div>

> [@jan](#):
>
> Added some more tests.

It appears MinGW (9.2.1) has a broken llround(), so using llround() is still not perfect ☹ See [MinGW - Minimalist GNU for Windows / Patches / #370 lround and llround functions are incorrect](https://sourceforge.net/p/mingw/patches/370/) This was reported in 2008 … Anyway, a number of the IEEE-754 tests fail as well ☹
