IEEE-754 half-even compatible

The C standard library provides families of round , lround and llround in C99. However, these functions do not comply with the IEEE-754 requirements, since they do not implement “banker rounding” from half to parity, as provided by IEEE. Half rounding rounds off rounding to the nearest even value if the fractional component is 0.5. The C99 standard instead sets half to zero, as indicated by cppreference.com

1-3) Computes the nearest integer value in arg (in floating point format), rounding half the cases from zero, regardless of the current rounding mode.

The usual ad-hoc way to implement rounding in C is to use the expression (int)(x + 0.5f) , which, despite being incorrect in strict IEEE-754 math, is usually translated by the compilers into the correct cvtss2si instruction. However, this, of course, is not a figurative assumption.

How can I implement a function that will span any floating point value with semi-parity semantics? If possible, the function should rely only on the semantics of the language and the standard library so that it can work with floating point types other than IEEE. If this is not possible, the answer defined in terms of the IEEE-754 bit representation is also acceptable. Designate any constants in terms of <limits.h> or <limits> .

+5
source share
6 answers

Around the number x, and if the difference between x and round (x) is exactly +0.5 or -0.5, and round (x) is odd, then round (x) is rounded in the wrong direction, so you subtract the difference from x.

+5
source

The C standard library provides families of round , lround and llround in C99. However, these functions are not compatible with IEEE-754, because they do not implement "rounding of bankers" from half to parity, as provided by IEEE ...

It makes no sense to talk about whether an individual feature is "IEEE-754 compliant." Compliance with IEEE-754 requires a set of data type operations with specific semantics. It does not require that these types or operations have specific names, and it does not require that only those operations be available. An implementation can provide any additional features that it wants, and still be compatible. If an implementation wants to provide rounded, round-random, rounded-from-zero, and trap-if-inexact, it can do it.

In fact, rounding is required for the IQE-754, as there are six operations:

convertToIntegerTiesToEven (x)

convertToIntegerTowardZero (x)

convertToIntegerTowardPositive (x)

convertToIntegerTowardNegative (x)

convertToIntegerTiesToAway (x)

convertToIntegerExact (x)

In C and C ++, the last five of these operations are tied to the trunc , ceil , floor , round and rint , respectively. C11 and C ++ 14 do not have a binding for the first, but roundeven will be used in future versions. As you can see, round actually one of the necessary operations.

However, roundeven not available in current implementations, which brings us to the next part of your question:

The usual ad-hoc way to implement rounding in C is to use the expression (int)(x + 0.5f) , which, although it is incorrect in strict IEEE-754 math, is usually translated by the compilers into the correct cvtss2si instruction. However, this, of course, is not a figurative assumption.

The problems with this expression are far superior to "rigorous IEEE-754 math." This is absolutely wrong for negative x , gives the wrong answer for nextDown(0.5) and turns all the odd integers in bin ** 2 23 into even integers. Any compiler that translates it to cvtss2si is horribly, horribly broken. If you have an example of this event, I would really like to see it.

How can I implement a function that will span any floating point value with semi-parity semantics?

As noted in the njuffa comment, you can make sure the rounding mode is set to the default and use rint (or lrint , since it seems like you really want to get an integer result) or you can implement your own rounding function by calling round and then correcting half the cases, for example gnasher729 . Once the n1778 bindings for C are accepted, you can use the roundeven or fromfp to perform this operation without the need to control the rounding mode.

+5
source

Use remainder(double x, 1.0) in the C standard library. This is independent of the current rounding mode.

The remaining functions calculate the remainder x REM y required by IEC 60559

remainder() is useful here because it matches OP bindings to even requirements.


 double round_to_nearest_ties_to_even(double x) { x -= remainder(x, 1.0); return x; } 

Test code

 void rtest(double x) { double round_half_to_even = round_to_nearest_ties_to_even(x); printf("x:%25.17le z:%25.17le \n", x, round_half_to_even); } void rtest3(double x) { rtest(nextafter(x, -1.0/0.0)); rtest(x); rtest(nextafter(x, +1.0/0.0)); } int main(void) { rtest3(-DBL_MAX); rtest3(-2.0); rtest3(-1.5); rtest3(-1.0); rtest3(-0.5); rtest(nextafter(-0.0, -DBL_MAX)); rtest(-0.0); rtest(0.0); rtest(nextafter(0.0, +DBL_MAX)); rtest3(0.5); rtest3(1.0); rtest3(1.5); rtest3(2.0); rtest3(DBL_MAX); rtest3(0.0/0.0); return 0; } 

Output

 x: -inf z: -inf x:-1.79769313486231571e+308 z:-1.79769313486231571e+308 x:-1.79769313486231551e+308 z:-1.79769313486231551e+308 x: -2.00000000000000044e+00 z: -2.00000000000000000e+00 x: -2.00000000000000000e+00 z: -2.00000000000000000e+00 x: -1.99999999999999978e+00 z: -2.00000000000000000e+00 x: -1.50000000000000022e+00 z: -2.00000000000000000e+00 x: -1.50000000000000000e+00 z: -2.00000000000000000e+00 tie to even x: -1.49999999999999978e+00 z: -1.00000000000000000e+00 x: -1.00000000000000022e+00 z: -1.00000000000000000e+00 x: -1.00000000000000000e+00 z: -1.00000000000000000e+00 x: -9.99999999999999889e-01 z: -1.00000000000000000e+00 x: -5.00000000000000111e-01 z: -1.00000000000000000e+00 x: -5.00000000000000000e-01 z: 0.00000000000000000e+00 tie to even x: -4.99999999999999944e-01 z: 0.00000000000000000e+00 x:-4.94065645841246544e-324 z: 0.00000000000000000e+00 x: -0.00000000000000000e+00 z: 0.00000000000000000e+00 x: 0.00000000000000000e+00 z: 0.00000000000000000e+00 x: 4.94065645841246544e-324 z: 0.00000000000000000e+00 x: 4.99999999999999944e-01 z: 0.00000000000000000e+00 x: 5.00000000000000000e-01 z: 0.00000000000000000e+00 tie to even x: 5.00000000000000111e-01 z: 1.00000000000000000e+00 x: 9.99999999999999889e-01 z: 1.00000000000000000e+00 x: 1.00000000000000000e+00 z: 1.00000000000000000e+00 x: 1.00000000000000022e+00 z: 1.00000000000000000e+00 x: 1.49999999999999978e+00 z: 1.00000000000000000e+00 x: 1.50000000000000000e+00 z: 2.00000000000000000e+00 tie to even x: 1.50000000000000022e+00 z: 2.00000000000000000e+00 x: 1.99999999999999978e+00 z: 2.00000000000000000e+00 x: 2.00000000000000000e+00 z: 2.00000000000000000e+00 x: 2.00000000000000044e+00 z: 2.00000000000000000e+00 x: 1.79769313486231551e+308 z: 1.79769313486231551e+308 x: 1.79769313486231571e+308 z: 1.79769313486231571e+308 x: inf z: inf x: nan z: nan x: nan z: nan x: nan z: nan 
+2
source

The float data float can represent all integers, but without fractions, ranging from 8388608.0f to 16777216.0f. Any float digits that are larger than 8388607.5f are integers and no rounding is required. Adding 8388608.0f to any non-negative float that is less than this will give an integer that will be rounded according to the current rounding mode (usually half to even). Then, subtracting 8388608.0f, you get the correct rounded version of the original (assuming that it is in a suitable range).

Thus, it should be possible to do something like:

 float round(float f) { if (!(f > -8388608.0f && f < 8388608.0f)) // Return true for NaN return f; else if (f > 0) return (float)(f+8388608.0f)-8388608.0f; else return (float)(f-8388608.0f)+8388608.0f; } 

and take advantage of the natural rounding of addition, without having to use any other circle-integer tool.

+1
source

The following is a simple implementation of the round-half to even program that follows the IEEE rounding standard.

Logic: error = 0.00001

  • number = 2.5
  • temp = floor (2.5)% 2 = 2% 2 = 0
  • x = -1 + temp = -1
  • x * error + number = 2.40009
  • round (2.40009) = 2

Note: The error here is 0.00001, that is, if 2.500001 occurs, then it will be rounded to 2 instead of 3

Python 2.7 implementation:

 temp = (number) rounded_number = int( round(-1+ temp%2)*0.00001 + temp ) 

C ++ implementation: (use the math.h function for the flooring function)

 float temp = (number) int rounded_number = (int)( (-1+ temp%2)*0.00001 + temp + 0.5) 

The result that this gives is as follows: acc. to standards:

(3.5) → 4

(2.5) → 2


Edit 1: As @Mark Dickinson noted in the comments. The error can be modified as required in your code to standardize it. For python, to turn it into the smallest possible float value, you can do the following.

 import sys error = sys.float_info.min 
+1
source

Starting with C ++ 11, there was a function in STL that performs half even rounding. If the floating point rounding mode is set to FE_TONEAREST (by default), then std::nearbyint will work.

C ++ Link for std :: nearint

0
source

Source: https://habr.com/ru/post/1232129/


All Articles