Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rounding a Double without using Round or Truncate

Tags:

c#

rounding

I was asked the following question and I didn't know how to answer it. I was wondering if anybody could help:

Using C#, and not using any Math functions (Round() and Truncate() aren't allowed) take the following double 3.009784654 and round it to 4 decimal places.

This was for an interview, not for a class project or homework. The interviewer seemed to be trying to get me to use mod, but I still couldn't think of how to do it.

Thanks!

like image 715
MattW Avatar asked Jul 02 '12 18:07

MattW


2 Answers

To truncate (since rounding rules were not specified): multiply by 1e4, cast to int, divide by 1e4.

like image 148
roken Avatar answered Nov 02 '22 18:11

roken


This is a bad interview question, because the task is generally impossible, given the expected tools, because most of the correct results cannot be represented. That is, the common floating-point type cannot represent the correct mathematical result, 3.0098, exactly. So it is impossible to calculate and return the correct answer. You would have to use some alternate mechanism, such as returning the value in a different type (scaled to an integer, in a string, in a decimal floating-point format, et cetera).

I suspect an answer the interviewer may have been expecting is to evaluate (int)(x*10000+.5)*.0001. This scales the value so that the desired results are integers (the rounding quantum, .0001, is scaled to 1), adds .5, truncates to an integer, and reverses the scaling. The combination of adding .5 and truncating is nearly equivalent to rounding, except it requires x to be positive, rounds ties away from zero, and has range/domain issues. Adjustments for those can be made depending on the situation and desired behavior. And, of course, the answers are generally slightly incorrect, due to the inability to represent exact results.

A more interesting answer is to evaluate (x*10000+0x1p52-0x1p52)*.0001. This scales the value, rounds it to an integer, using the current floating-point rounding mode, and undoes the scaling. The rounding is effected because the common double type has 53 bits in its significand, so, when the high bit has value 0x1p52 (252), the low bit’s value is 0x1p0 (20, also known as 1). In order to produce the result of adding 0x1p52, the significand must be rounded, which is done automatically in the floating-point hardware. Then subtracting 0x1p52 removes the added number, leaving the rounded value. As before, there are caveats with range/domain and ensuring the compiler does double-precision arithmetic and not something more, and adjustments can be made. However, it is faster on some hardware than converting to integer and back.

Using fmod or string conversions has worse performance because they require division, which is slow on most common processors.

like image 27
Eric Postpischil Avatar answered Nov 02 '22 17:11

Eric Postpischil