6

I want to write a function to round a double to an int using Banker's Rounding method (round half to even: http://en.wikipedia.org/wiki/Rounding#Round_half_to_even), like:

int RoundToInt(double x);

How can I do that?

Update:

The best I can get is this:

int RoundToInt(double x)
{
  int s = (int)x;
  double t = fabs(x - s);

  if ((t < 0.5) || (t == 0.5 && s % 2 == 0))
  {
    return s;
  }
  else
  {
    if (x < 0)
    {
      return s - 1;
    }
    else
    {
      return s + 1;
    }
  }
}

But this is slow and I'm not even sure if it is accurate.

Is there some quick and accurate way to do this.

EFanZh
  • 2,357
  • 3
  • 30
  • 63
  • [StackOverflow is not your personal research assistant.](http://meta.stackexchange.com/a/128553) – Adam Casey Jun 01 '12 at 16:12
  • @AdamCasey I need this to be solved, and I failed to solve it, so I came he for some help. Or is my question a really silly one? – EFanZh Jun 01 '12 at 17:19
  • Your need and question is not the problem. Check the link in my comment; you need to show that you a. Put some effort into researching the problem, b. Attempted to write some code yourself, and c. Attempted to debug your code when it has failed – Adam Casey Jun 01 '12 at 18:02
  • @AdamCasey I've done my research and I don't think it's helpful. My code is slow and clumsy, I don't know if it is accurate. This is the best I can do. – EFanZh Jun 02 '12 at 02:40
  • That is what a good question looks like now. I changed my down vote to an up vote! – Adam Casey Jun 02 '12 at 12:24

2 Answers2

5

Use the standard lrint function; in the default rounding mode, it gives exactly the result you want.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • You sure about that? The man page you linked to doesn't mention what happens when the number is exactly between two integers. –  Jun 01 '12 at 06:14
  • 1
    `lrint` obeys the current rounding mode. The default rounding mode is round-to-nearest, also known as round-to-even, or "bankers" rounding. – R.. GitHub STOP HELPING ICE Jun 01 '12 at 06:14
  • I'm using VC++, it doesn't support `lrint`. – EFanZh Jun 01 '12 at 06:19
  • Does it support `rint`? Using `rint` then casting the result to an integer type (or letting it get implicitly converted by an assignment) should work just as well. – R.. GitHub STOP HELPING ICE Jun 01 '12 at 06:20
  • No, it seems that VC++ only support C90 standard. – EFanZh Jun 01 '12 at 06:22
  • This won't banker's round in any realistic situation. The value you pass to `lrint` will already by rounded because it's in a non-exact (floating point) format. Double-rounding is not banker's rounding. – David Schwartz Jun 01 '12 at 06:23
  • You're making additional assumptions. – R.. GitHub STOP HELPING ICE Jun 01 '12 at 06:25
  • It's a terrible idea to rely on something that depends on global state. For example, a library might modify the rounding mode, and an old version of Visual Studio was even known to [temporarily modify it when casting to an int](https://software.intel.com/en-us/articles/fast-floating-point-to-integer-conversions). If that happens in another thread while this code is running, you could get the wrong result. – Arthur Tacca Apr 10 '18 at 09:32
  • @ArthurTacca: Rounding mode is thread-local state, not global. – R.. GitHub STOP HELPING ICE Dec 19 '19 at 02:30
1
double decimal = x % 1;
if(decimal < 0.5) return (int)x;
if(decimal > 0.5) return (int)x + 1;
return (int)x + ((int)x % 2 == 1 ? 1 : 0);
  • I don't think this will work correctly. You're allowing accumulated error to change your rounding method, and that is not correct. For example `round(5.0 / 2.0)` *must* use the rounding rule for `2.5`. But if your round function sees that as `2.4999999993` (due to rounding in the division), it will use the wrong rule. – David Schwartz Jun 01 '12 at 06:12
  • @DavidSchwartz: Why do you claim it should work this way? I see no basis for making that judgement. – R.. GitHub STOP HELPING ICE Jun 01 '12 at 06:14
  • That is the definition of banker's rounding. "A perfect half is rounded to the nearest even number." If you don't ensure you round 7/2 to 4, it ain't banker's rounding. – David Schwartz Jun 01 '12 at 06:14
  • 1
    I'm talking about your claim that it should treat 2.4999999993 as 2.5; I see no basis for that. Moreover your claim that 5.0/2.0 could be 2.4999999993 is nonsense; 5.0/2.0 is always exact. – R.. GitHub STOP HELPING ICE Jun 01 '12 at 06:16
  • That's why I said *if*. I'm trying to show with a simple example what the problem is. If other functions round so that you don't see an exact half, the banker's rounding function will do the wrong thing. The same thing could happen if a bunch of fractions were added together. – David Schwartz Jun 01 '12 at 06:17
  • The point of banker's rounding is to ensure that if the exact result would have been an even half, the result is rounded to the nearest integer. This function won't do that because it acts on whether the rounded result is an even half, not the exact result. (How you fix it depends on context and usually involves using some delta in the check for .5 -- this will fail to banker's round in many cases where it should do so.) – David Schwartz Jun 01 '12 at 06:18
  • 1
    Double-rounding is not a solution to the problem; for every case it "fixes", there are just as many cases where it causes rounding in the wrong direction. The moral of the story is that floating point is never a valid format for money. – R.. GitHub STOP HELPING ICE Jun 01 '12 at 06:19
  • @R..: I agree. But the function above is just wrong. It won't actually banker's round. It tests whether the *rounded* result is an even half, which is simply broken. You have to banker's round the *unrounded* result -- double rounding is not banker's rounding. (The fix is usually to use a delta in the test for .5, and correctly computing this delta is non-trivial.) – David Schwartz Jun 01 '12 at 06:22
  • @DavidSchwartz you're saying that the %1 operation will cause precision problems? –  Jun 01 '12 at 06:23
  • @DavidSchwartz: I don't see any justification for calling it a rounded result. It's just a number. If it is the result of an inexact computation, then yes, it's rounded, but your proposal to "fix" it does not work. – R.. GitHub STOP HELPING ICE Jun 01 '12 at 06:23
  • @bdares: There is no `%` operator for floating point. `fmod` is the equivalent, and it's an exact operation. – R.. GitHub STOP HELPING ICE Jun 01 '12 at 06:24
  • @R..: Whatever you want to call it, it's not an exact representation. So comparing it exactly to 0.5 will not determine accurately if the exact result would have been less than or greater than 0.5 -- this is what is required for banker's rounding. – David Schwartz Jun 01 '12 at 06:26
  • @DavidSchwartz: Any floating point number has an exact value, and comparing it in this manner is completely valid. What's not valid is double-rounding. If the value is already the result of rounding (which it may or may not be; OP provided no such information to us) then attempting to perform "banker's rounding" on it is a second rounding step (double rounding) and thus invalid. There's no way around this. – R.. GitHub STOP HELPING ICE Jun 01 '12 at 06:29