1

I would like to convert a float or double to a decimal fixed point integer in C language. I am searching the most appropriate (robust) solution for this problem, considering C language's specification.

The problem for example is along the lines of this:

double d = 15.6;
int    i;
...
i = (int)(d * 10.0); /* I am excepting getting 156 in 'i' */

Relevant elements of the standard (using a C99 one, ISO/IEC 9899:TC3, see this question for download) as far as I see:

  • 6.3.1.4 / 1: When a finite value of real floating type is converted to an integer type other than _Bool, the fractional part is discarded (i.e., the value is truncated towards zero). (...)
  • 6.4.4.2 / 3: (...) For decimal floating constants, (...) the result is either the nearest representable value, or the larger or smaller representable value immediately adjacent to the nearest representable value, chosen in an implementation-defined manner. (...)

The 15.6 in the example has not got an exact representation in IEEE double, so I would except d to get something slightly above, or slightly below. The problem is with the "slightly below" part, then i in the example wouldn't get the excepted result (would get 155 instead).

My obvious take would be something like this (considering zero or positive values only):

i = (int)(d * 10.0 + 0.5);

At least if I interpreted the C standard correctly. I ask because due to the "implementation-defined" behavior, one may experience a consistent result while in the reality some other implementation may break the program, so trial and error is not an adequate method for finding an appropriate solution.

In particular the following question relates this problem, which I believe has an incorrect accepted answer.

11
  • The C standard does not mandate the use of IEEE floating point. It's theoretically possible d is represented as some crazy number like 15.65. This becomes much more likely as d gets larger. I would say d is already wrong and you should be using an arbitrary precision library from the beginning. Commented Dec 10, 2014 at 14:35
  • @Kevin: I just mentioned IEEE since even in IEEE this example shows the problem. If you throw in the "heavy artillery" going strict by the wording of the C standard, of course you soon have not just one big gaping hole. An arbitrary precision library may not always be feasible, consider an embedded target, for example. Commented Dec 10, 2014 at 14:44
  • Well, then you have to decide which you value more: performance or correctness? Commented Dec 10, 2014 at 14:47
  • I would suspect (void) fesetround(FE_DOWNWARD); followed by i = (int) nearbyint(d);, but I'm hardly a floating point guru so I don't dare post this as an answer. Commented Dec 10, 2014 at 14:49
  • @Kevin: C language floats and doubles shouldn't be that bad! Or are they? (so that this problem is impossible to be solved using standard C assuming worst-case) Commented Dec 10, 2014 at 14:51

1 Answer 1

2

C99 specifies a round function for exactly this purpose. Use that, then cast to int.

UPDATE: for C89, you could try

double y = floor(x);
double z = x == y ? x : floor(2.0*x-y);

This should give the same as C99 round, except that negative numbers with fractional parts equal to 0.5 will be rounded upwards (like Java), and zeros may be signed incorrectly (this is based on a similar trick due to Arch Robinson).

Sign up to request clarification or add additional context in comments.

5 Comments

Huh, found it (I mean in the standard)! I am a strict C89 guy working in embedded, didn't even pop in my mind that C99 could have this. I accept it since I referred to a C99 standard in my post, so I really can't say anything wrong with this solution. It would be nice though if the answer was eventually edited to include a C89 compliant solution as well.
Interesting (the article, too), and seems functional, OK by C89. By the way z = floor(x + 0.5) why wouldn't be sufficient? C89 (and anything above) defines floor to return the largest integer not greater than it's operand, so it should work OK for negatives, too. Arch Robinson was using trunc, which for negatives rounds towards zero (similar to my take if I go by 6.3.1.4/1 from the standard - it is an other story that the existence of fesetround in C99 makes this point of the standard smelling bogus).
floor(x+0.5) might be sufficient for your case, but not generally: the two cases where it causes problems are x = 0.49999999999999994 (the floating point number below 0.5), where adding half will round to 1, and odd values of absolute value between 2^52 and 2^53, where adding half will round to the next even integer.
Also, I seem to recall that C89 doesn't specify the truncating behaviour that is in C99 (i.e. it allows the choice of either the integer above or below), but you should double check that.
The truncating exists in the C89 draft I have, I even formed the question based on that. I did not refer the C89 draft since it is (probably?) not that easy to find like the C99 edition I cited from. Nice little details, though in your previous post. As I see FP, one just shouldn't rely on anything that precise when dealing with FP, rounding also working like "I need values reasonably around 1 to round to integer 1": after all any FP calculation will bring in inaccuracies accumulating. It is an other thing though when it is for compiler design.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.