# C++ rounding half-even

Hi,
Is there a C/C++ equivalent function for rounding half-even? I need identical function to Java's BigDecimal.setScale(int scale, BigDecimal.ROUND_HALF_EVEN);
So 20.225 = 20.22
20.235 = 20.24
Thanks.
###### Who is Participating?

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

OwnerCommented:
``````// bankers-round.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <iomanip>
#include <cstring>
#include <math.h>
using namespace std;

float bround(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;
}
/**
* Main entry to the program.
*/
int main(void) {
float test1 = 20.225;
float test2 = 20.235;

cout << "Testing " << test1 << endl;
cout << (bround(test1 * 100)/100) << endl;
cout << "Testing " << test2  << endl;
cout << (bround(test2*100)/100) << endl;
return 0;
}
``````

Testing 20.225
20.22
Testing 20.235
20.24

https://stackoverflow.com/questions/32746523/ieee-754-compliant-round-half-to-even
0

Experts Exchange Solution brought to you by

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

DeveloperAuthor Commented:
David,
This works! Whoever thought of this method  is genius. Thanks for picking the answer (there were many). Can you explain the method? I did not fully understand. Also, I get compiler warning assigning double to float.
0
Commented:
it only works with float.

for double you may use this:

``````double bround2dec(double d)
{
// save sign
int sgn = (d < 0.)? -1 : 1;
double dabs = d * sgn;
// convert to 64 bit integer and factor 1000
long long ll1000 = (long long)(dabs * 1000);

// get last digit
int rc = (int) ll1000%10;
// if  the first decimal digit which decides whether to round up or down is not 5
// we simply can round by adding 5 and cut by doing integer rounding
if (rc != 5)
return ((ll1000 + 5)/1000.) * sgn;

// if coming here we extract the digit which was subject of being rounded
int rr = (int) ((ll1000-rc)/10)%10;
// if the digit is odd we round up else we round down
return  ((rr+1)%2 == 0)? ((ll1000 + 5)/1000.) * sgn : ((ll1000-5)/1000.) * sgn;
}

// you can use it like

#include <sstream>
#include <iostream>
#include <string>
....

double d1 =  20.225;//  225.885;
double d2 =  20.235;//  -0.065;

std::ostringstream oss;
// std::ios::fixed means that the number of decimal digits in the fraction given by the setprecison was fixed
// otherwise the precision means the number of significant digits of the decimal number
oss << d1 << " was rounded to " << std::setiosflags( std::ios::fixed ) << std::setprecision(2) << bround2dec(d1) << "  "
<< d2 << " was rounded to " << std::setiosflags( std::ios::fixed ) << std::setprecision(2) << bround2dec(d2);
std::string s = oss.str();
``````

Sara
1
Commented:
the compiler warning will arise if you were using a double where a float is expected. for example test1*100 is a double for most compilers as the 100 has to be converted to a floating point number and double is the default. if you would use test1*100. the compiler should handle the result to be a float because the test1 explicitly was defined as a float and the 100. easily can be initialized as a float as well as it has only 3 significant digits what is within the precision range of a float.

Can you explain the method?
the explanation was given in the link phoffric has posted:

The float data type can represent all whole numbers, but no fractions, within the range 8388608.0f to 16777216.0f. Any float numbers which are larger than 8388607.5f are whole numbers, and no rounding will be necessary. Adding 8388608.0f to any non-negative float which is smaller than that will yield a whole number which will be rounded according to the current rounding mode (typically round-half-to-even). Subtracting 8388608.0f will then yield a properly-rounded version of the original (assuming it was in a suitable range).

in other words: (positive) integers stored in a float variable are in a special range of the float internal coding. if you add 8388608.0f to any arbitrary float that you want to round properly at the integer boundary (note: test1 was multiplied with 100) you would move the float number into the integer range area and therefore it was rounded correctly by using the standard half-to-even round algorithm. if you finally subtract the 8388608.0f you will get the original float back but rounded.

Sara
0
\Commented:
>> in the link phoffric has posted:
Could you provide a link for the quote.
0
Commented:
it is stackoverflow.com/questions/32746523/ieee-754-compliant-round-half-to-even

the quoted text is in the final part of this lengthy thread posted by a member called 'supercat'.

you can find it easily by searching for "8388608.0f"

Sara
0
DeveloperAuthor Commented:
Thanks, Sara. I will try it out and post results.
0
DeveloperAuthor Commented:
Sara, I got results below:
David's code:
Testing -20.125
-20.12
Testing -701.252
-701.25

-20.125 was rounded to -20.12  -701.25 was rounded to -701.26 should be -701.25
Is it because my system is 32 bit?
0
Commented:
Is it because my system is 32 bit?

no, bitness shouldn't make any difference (beside that the 8388608.0f is valid for 32-bit float only).

-701.252 must be rounded to .701.25 because the 3rd digit after decimal point is not a '5'.

the return of my function is

((ll1000 + 5)/1000.) * sgn;

what is (701252 + 5)/1000. * (-1)

what is (701257/1000. * (-1)

what is 701.257 * (-1)

what is - 701.26 rounded to 2 decimals.

so, there is a bug in my code which is the division by 1000. (and not 1000) what makes the result a double instead of an integer.

if you change

``````return ((ll1000 + 5)/1000.) * sgn;
``````

by

``````return ((ll1000 + 5)/1000) * sgn;
``````

it should work correctly.

Sara
0
DeveloperAuthor Commented:
OK. Thanks. I will inform you of results.
0
\Commented:
@Sara,

>> the explanation was given in the link phoffric has posted:
0
DeveloperAuthor Commented:
@phorric
Sara most likely meant: explanation is given in link posted to you. Go thru the thread.
0
Commented:
sorry, phoffric.

the link i mentioned was from david at #a42521142

Sara
0
DeveloperAuthor Commented:
Sara,
That does not work either. The output is 701.00.
-20.125 was rounded to -20.12   -701.252 was rounded to -701.00
and compiler warning:
warning C4244: 'return' : conversion from '__int64' to 'double', possible loss of data
because: return ((ll1000 + 5)/1000) * sgn; is _int64.
I tweaked to: return ((ll1000-5)/1000.) * sgn; //Comments?
The output is: -20.125 was rounded to -20.12   -701.252 was rounded to -701.25.
With +ve value: -20.125 was rounded to -20.12   701.252 was rounded to 701.25
The true test of rounding half-even:
20.145 was rounded to 20.14   20.135 was rounded to 20.14
But this does not hit above code.
Final test with David's output:
Testing -701.252
-701.25
Testing 701.252
701.25

-701.252 was rounded to -701.25   701.252 was rounded to 701.25
which matches.
Finally, instead of cout, how do you store your output in a double variable with precision 2?
0
DeveloperAuthor Commented:
Thanks all. I find David's solution more robust and fits my need.
0
\Commented:
>> sorry, phoffric.   the link i mentioned was from david

I was trying so hard remembering where/when I may have made the statement.

Aha, OK, well in the future, try not to take my name in vain. :)
0
Commented:
That does not work either.

the corrected code is

``````#include <limits.h>

double bround(double d)
{
int sgn = (d < 0.)? -1 : 1;
// make it positive
double dabs = d * sgn;
// convert to 64-bit integer

// note a 64-bit integer has a range from -2^63 to 2^63
// while a double is from approximately -1.8 × 10^308 to 1.8 × 10^308
// if that is an issue you may not use the bround function
if (d < (double)(LLONG_MIN/1000) || d > (double)(LLONG_MAX/1000))
{
return d;
}

// convert to 64-bit integer with 3 fractional digits
long long ll1000 = (long long)(dabs * 1000);

// extract last digit
int rc = (int) ll1000%10;
// if not 5 we can do a 'normal rounding' ...
if (rc != 5)
{
// ... by adding 5 to the long long and do an integer division by 10
// so for 0 to 4 we get a maximum of 9 and therefore round down and
// for 6 to 9 we pass the next 10 boundary and round up
// by dividing with 100. we get a double with 2 decimals

return ((ll1000 + 5)/10)/100. * sgn;
}
// extract the forelast digit
int rr = (int) ((ll1000-rc)/10)%10;
// check if even or odd by using modulo 2
// and either add 5 for odd digits or subtract 5 for even digits
// what both means to round to the nearest even integer
return  ((rr+1)%2 == 0)? ((ll1000 + 5)/10)/100. * sgn : ((ll1000-5)/10)/100. * sgn;
}
``````

Finally, instead of cout, how do you store your output in a double variable with precision 2?

decimal precision is an output feature. a double has two binary components the mantissa and the exponent. with that you can't fix it to 2 decimal places. for nearly all cases (beside of multiples of 2 numbers)  a double is either a small amount less or a small amount greater than the a decimal number rounded to 2 decimal places. if you look at the doubles returned by the bround function with the debugger, you will see that the doubles either display like 20.23500000000000... or like -225.229999999999... . the ... digits displayed after 16 significant digits in general are different to the '0' or '9' sequence left from it.  that is because a double only has a precision of 16 decimals (approximately 15.95 decimal digits (log10(2^53)) and 53 bits is the length of the mantissa in bits, see https://en.wikipedia.org/wiki/Double-precision_floating-point_format).

so, the only ways to store exactly with precision 2 is either to store strings or to store integers by multiplying the double with 100.

note, a double has 8 bytes, a 32-bit integer 4 bytes and a char buffer would take number of digits + 1 for the decimal point + 1 for the terminating zero character, e. g. "20.24" would need a buffer of 6 bytes.

Sara
1
DeveloperAuthor Commented:
Thanks for clarification. That is exactly what i found.
0
DeveloperAuthor Commented:
@sara
I modified your code and now it works for me for large values (which did not work in float method)
``````double bround2dec(double d)
{
// save sign
int sgn = (d < 0.)? -1 : 1;
double dabs = d * sgn;
char buffer[32];
double retVal =0.00;
// convert to 64 bit integer and factor 1000 - This could be an in parameter depending on scale
long long ll1000 = (long long)(dabs * 1000);

// get last digit
long long rc = (long long) ll1000%10;
// if  last digit is 5 and before it is even,  subtract 5, else add 5
// so 20.905 = 20.90 and 20.915 = 20.92
if (rc == 5)
{
if((ll1000-5)/10%10%2 == 0)
retVal = (ll1000 -5)/1000.* sgn;
else
retVal = (ll1000+5)/1000.* sgn;
}
else
retVal = dabs* sgn;

sprintf(buffer, "%0.2f", retVal);
return  atof(buffer);
}
``````
I tested out in my app and seems to work for large numbers
Do you see any issues?
0
Commented:
no there are no issues with the solution as it is straight forward and is checking directly the 2nd and 3rd digit after period.

Sara
0
###### It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C++

From novice to tech pro — start learning today.