154

I have a double (call it x), meant to be 55 but in actuality stored as 54.999999999999943157 which I just realised.

So when I do

double x = 54.999999999999943157;
int y = (int) x;

y = 54 instead of 55!

This puzzled me for a long time. How do I get it to correctly round?

Samuel Liew
  • 76,741
  • 107
  • 159
  • 260
midnightBlue
  • 1,735
  • 2
  • 13
  • 13
  • 1
    You could add 0.5 to the number and then do your cast to let it truncate to an int. Do you need to round negative numbers? – Blastfurnace Mar 14 '12 at 03:03
  • 6
    You can use this preprocessor definition: `#define ROUND_2_INT(f) ((int)(f >= 0.0 ? (f + 0.5) : (f - 0.5)))` – c00000fd May 01 '14 at 01:41
  • Actually 54.999999999999943157 is 8 ULPs below _exactly representable_ 55 if by `double` you mean binary64 from IEEE 754. So this is not how 55 is actually stored, it's the consequence of how imprecise your calculation of it was. – Ruslan Jul 15 '16 at 12:07
  • 3
    Adding +0.5 to a negative input before turning it into an int will give the wrong answer. The correct quick-and-dirty way is to test the input sign for <0, and then SUBTRACT 0.5 from the negative inputs before turning them into an int. Most of the following answers do not explain this properly. Note high-accuracy procedures should use the new slower "round()" function. – DragonLord Aug 13 '16 at 22:37
  • 1
    See also [Convert double to int in C++ without round down errors](http://stackoverflow.com/questions/23374153/convert-double-to-int-in-c-without-round-down-errors). – Vadzim Aug 14 '16 at 18:35
  • For me, the program to below prints "55". Am I doing something differently than you? `#include using namespace std; int main() { float x = 55; int y = (int) x; cout << y; }` – plafratt Nov 09 '16 at 18:35
  • @plafratt It may be possible that your system's implementation of `float` is such that `float x = 55` is stored as a value slightly greater than `55` instead. In this case `(int) x` would seem to 'round down' correctly to `55`, and not `54`, as in my case. – midnightBlue Nov 09 '16 at 21:37
  • Oh ok. For reference, the code below gives this output: "55 55.00000000000000000000". `#include #include using namespace std; int main() { float x = 55; int y = (int) x; cout << y << " " << fixed << setprecision(20) << x << "\n"; }` Changing the "float x = 55;" to "float x = 55.1;" gives me the output "55 55.09999847412109375000" – plafratt Nov 10 '16 at 17:40
  • https://www.cs.cmu.edu/~rbd/papers/cmj-float-to-int.html – raaj Apr 15 '20 at 20:59
  • the question in not equivalent to referenced question as it asking for type conversion as well – hgrey Jul 19 '20 at 20:22

5 Answers5

172

add 0.5 before casting (if x > 0) or subtract 0.5 (if x < 0), because the compiler will always truncate.

float x = 55; // stored as 54.999999...
x = x + 0.5 - (x<0); // x is now 55.499999...
int y = (int)x; // truncated to 55

C++11 also introduces std::round, which likely uses a similar logic of adding 0.5 to |x| under the hood (see the link if interested) but is obviously more robust.

A follow up question might be why the float isn't stored as exactly 55. For an explanation, see this stackoverflow answer.

Moritz
  • 4,565
  • 2
  • 23
  • 21
  • it would work equally as well with just int y=x+0.5; though right? – midnightBlue Mar 14 '12 at 03:09
  • 3
    I wouldn't use the phrase "round down", because that could be interpreted as rounding `-0.5` to `-1`. –  Mar 14 '12 at 03:16
  • As @Verax mentioned, this does not work for negative numbers as cast truncates toward 0. You may use the same logic with 'floor' instead as it truncates towards -infinity. – mehfoos yacoob Aug 14 '13 at 10:56
  • This answer is wrong. It will give you wrong answer if the number is negative. – Nick X Tsui May 15 '14 at 02:59
  • @Moritz; Oh sorry, I will upvote you. My apologies. – Nick X Tsui May 15 '14 at 04:43
  • 1
    -1 this is a hacky solution at best. zik's answer is much better as it uses a library function: works on negative numbers, doesn't do weird casting and it's explicit about it's intention. – Henry Henrinson Oct 02 '14 at 14:33
  • @HenryHenrinson ``ceil()``/``floor()`` also adds another level to the call stack (although I suspect that a modern compiler will inline it for you,) makes you include a library header and generally introduces more overhead. I don't think one is necessarily worse than the other without knowing what the OP is trying to accomplish. Also, I don't think casting from floating-point to int explicitly is that "weird"...whether you see it or not, that type conversion has to happen somewhere along the line :) – Moritz Oct 02 '14 at 18:38
  • @Moritz One can perform rounding and still return a double. Regardless, the point here is that the code is not explicit about what sort of casting it does. Does it take floor, ceil, rounds it, or does it just interpret the bits at the memory location referenced by the double as ints? Writing the code like this needlessly makes the reader remember what the conversion rules are. I agree that there are places where this code makes sense, but imagine someone googles "how to round c++" and ends on this page. IMO, the best answer for that situation is the one below. – Henry Henrinson Oct 03 '14 at 16:09
  • I prefer positive numbers – jacknad Apr 14 '16 at 20:41
  • 6
    What do you mean by `stored as stored as 54.999999...`? `55` is exactly representable in binary32 from IEEE 754. Its representation is 0x425c0000. As you can see, it's _more than exact_: It has plenty of digits to store some fractional part you add to it. And it's especially true of `0.5`, which is a power of two. – Ruslan Jul 15 '16 at 12:03
  • 3
    This is inaccurate & possibly slow. See clang-tidy for an explanation : http://clang.llvm.org/extra/clang-tidy/checks/misc-incorrect-roundings.html – ACyclic Nov 02 '16 at 13:36
  • @ACyclic unfortunately, the link you provided is dead. – Pac0 Aug 31 '18 at 08:57
  • 2
    @Pac0 Updated link on why this is inaccurate : http://releases.llvm.org/5.0.2/tools/clang/tools/extra/docs/clang-tidy/checks/misc-incorrect-roundings.html – ACyclic Nov 16 '18 at 18:47
  • 1
    @ACyclic That link just states it's bad but doesn't provide any suggestions to the problem. – markokstate Jun 28 '19 at 15:03
71

Casting is not a mathematical operation and doesn't behave as such. Try

int y = (int)round(x);
BaCaRoZzo
  • 7,502
  • 6
  • 51
  • 82
MK.
  • 33,605
  • 18
  • 74
  • 111
  • 11
    The cast is unnecessary; the result will be implicitly converted to `int`. – Keith Thompson Mar 14 '12 at 03:04
  • 11
    I could never remember the C type promotion rules and I think that it doesn't hurt to state type casts explicitly everywhere, them being such a sticky issue. – MK. Mar 14 '12 at 03:10
  • What library is `round` part of? AFAIK it's not in the C++ standard library. `ceil` and `floor` are available. – Steve Fallows Mar 14 '12 at 03:13
  • 5
    @Steve #include , STANDARDS The round() , lround() , and llround() functions conform to ISO/IEC 9899:1999(E). – MK. Mar 14 '12 at 03:14
  • @MK - Well that's actually C not C++ right? I referred to this: http://www.cplusplus.com/reference/clibrary/cmath/ – Steve Fallows Mar 14 '12 at 03:17
  • 2
    @SteveFallows: It exists in C++11. – GManNickG Mar 14 '12 at 03:24
  • 9
    @MK.: It's not a promotion, it's just an implicit conversion. An assignment, initialization, or parameter passing will implicitly convert any numeric type to any other numeric type. Unnecessary casts can be harmful. For example, `n = (int)round(x);` looks ok -- but what if `n` is of type `long`? – Keith Thompson Mar 14 '12 at 06:23
  • 3
    In both C99 and C++11, ```double round (double x);``` Therefore, the cast is indeed necessary. Link:http://www.cplusplus.com/reference/cmath/round/ – Qiangzini Jan 02 '20 at 23:00
  • 2
    if you use `std::round(double arg)` cast is necessary; but if you use `std::lround(double arg)` cast is not needed – simplename Apr 06 '22 at 02:37
10

Casting to an int truncates the value. Adding 0.5 causes it to do proper rounding.

int y = (int)(x + 0.5);
Pubby
  • 51,882
  • 13
  • 139
  • 180
  • 19
    not valid for negative numbers, as mentioned in one of the comments above. Can use 'floor' instead of cast if you are doing this. – mehfoos yacoob Aug 14 '13 at 10:54
5

It is worth noting that what you're doing isn't rounding, it's casting. Casting using (int) x truncates the decimal value of x. As in your example, if x = 3.9995, the .9995 gets truncated and x = 3.

As proposed by many others, one solution is to add 0.5 to x, and then cast.

-9
#include <iostream>
#include <cmath>
using namespace std;

int main()
{
    double x=54.999999999999943157;
    int y=ceil(x);//The ceil() function returns the smallest integer no less than x
    return 0;
}
Luke
  • 11,426
  • 43
  • 60
  • 69
zik
  • 23
  • 1