/* By Pedro Gimeno Fortea, donated to the public domain */

#include <assert.h>
#include <math.h>
#include <stdio.h>


double
rint(double x)
{
	double r;
	if (x <= 0.0)
	{
		if (x == 0.0)
			return x;
		r = floor(x += 0.5);
		if (r != x || x == 0.0)
			return x >= 0.0 ? -0.0 : r;
		return floor(x * 0.5) * 2.0;
	}
	r = ceil(x -= 0.5);
	if (r != x)
		return x <= 0.0 ? 0.0 : r;
	return ceil(x * 0.5) * 2.0;
}



int test_equal_z(double a, double b)
{
	char a1[20];
	char b1[20];
	sprintf(a1, "%+g", a);
	sprintf(b1, "%+g", b);
	return a == b && a1[0] == b1[0];
}

int main()
{
	assert(rint( 2.5) ==  2.0);
	assert(rint( 2.4) ==  2.0);
	assert(rint( 2.6) ==  3.0);
	assert(rint( 1.5) ==  2.0);
	assert(rint( 1.4) ==  1.0);
	assert(rint( 1.6) ==  2.0);
	assert(rint(-2.5) == -2.0);
	assert(rint(-2.4) == -2.0);
	assert(rint(-2.6) == -3.0);
	assert(rint(-1.5) == -2.0);
	assert(rint(-1.4) == -1.0);
	assert(rint(-1.6) == -2.0);

	/* Test minus zero */
	assert(test_equal_z(rint( 0.0),  0.0));
	assert(test_equal_z(rint(-0.0), -0.0));
	assert(test_equal_z(rint( 0.5),  0.0));
	assert(test_equal_z(rint(-0.5), -0.0));

	/* Corner cases for IEEE double */
	/* Last float having fractional part */
	assert(rint(-4503599627370495.5) == -4503599627370496.0);
	assert(rint( 4503599627370495.5) ==  4503599627370496.0);
	assert(rint(-4503599627370494.5) == -4503599627370494.0);
	assert(rint( 4503599627370494.5) ==  4503599627370494.0);
	/* abs value strictly greater than 0.5 should be rounded away from 0 */
	/* (these numbers are 0.5 + 1 ulp) */
	assert(rint( 0.5000000000000001) ==  1.0);
	assert(rint(-0.5000000000000001) == -1.0);
	/* abs value strictly less than 0.5 should be rounded towards 0 */
	/* (these values plus 1 ulp equal 0.5) */
	assert(test_equal_z(rint( 0.49999999999999994),  0.0));
	assert(test_equal_z(rint(-0.49999999999999994), -0.0));
	/* denormals */
	assert(test_equal_z(rint( 5e-324),  0.0));
	assert(test_equal_z(rint(-5e-324), -0.0));
	assert(test_equal_z(rint( 1e-322),  0.0));
	assert(test_equal_z(rint(-1e-322), -0.0));

	return 0;
}
