0

I want to truncate floor number to be 3 digit decimal number. Example:

input : x = 0.363954;
output: 0.364

i used

double myCeil(float v, int p)
{
  return int(v * pow(float(10),p))/pow(float(10),p );
}

but the output was 0.3630001 . I tried to use trunc from <cmath> but it doesn't exist.

Fadwa
  • 1,717
  • 5
  • 26
  • 43

4 Answers4

1

Floating-point math typically uses a binary representation; as a result, there are decimal values that cannot be exactly represented as floating-point values. Trying to fiddle with internal precisions runs into exactly this problem. But mostly when someone is trying to do this they're really trying to display a value using a particular precision, and that's simple:

double x = 0.363954;
std::cout.precision(3);
std::cout << x << '\n';
Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • I don't want to display it, i need it in my comparison process using (<, >, and ==) so it's very sensitive to 0.00001 increase. – Fadwa Dec 15 '13 at 14:11
  • 1
    @Misaki - sounds like an XY problem. Instead of describing the technique that you're thinking about, describe the problem that this technique is supposed to address. There's usually a better way than fiddling with precision (largely because fiddling with precision is tricky). – Pete Becker Dec 15 '13 at 14:17
  • I'm comparing the circularity of two blobs in image. i calculated the circularity by this piece of code `float GetCircularity(vector hull,float area, Point centroid) { float circularity = 0; for(int i = 0 ; i < hull.size() ; i++){ circularity += EuclideanDist(centroid,hull[i]); } return pow(circularity/hull.size(),2) / area; }` – Fadwa Dec 15 '13 at 14:20
  • @Misaki - this is outside my area of expertise, so I can't offer any detailed help. But this calculation looks like it's something that's been done many times, and there's a good chance that someone has posted something helpful. – Pete Becker Dec 15 '13 at 14:29
0

The function your looking for is the std::ceil, not std::trunc

    double myCeil(double v, int p)
    {
        return std::ceil(v * std::pow(10, p)) / std::pow(10, p);
    }

substitue in std::floor or std::round for a myFloor or myRound as desired. (Note that std::round appears in C++11, which you will have to enable if it isn't already done).

SirGuy
  • 10,660
  • 2
  • 36
  • 66
0

It is just impossible to get 0.364 exactly. There is no way you can store the number 0.364 (364/1000) exactly as a float, in the same way you would need an infinite number of decimals to write 1/3 as 0.3333333333...

You did it correctly, except for that you probably want to use std::round(), to round to the closest number, instead of int(), which truncates.

Comparing floating point numbers is tricky business. Typically the best you can do is check that the numbers are sufficiently close to each other.

Are you doing your rounding for comparison purposes? In such case, it seems you are happy with 3 decimals (this depends on each problem in question...), in such case why not just

bool are_equal_to_three_decimals(double a, double b)
{
   return std::abs(a-b) < 0.001;
}

Note that the results obtained via comparing the rounded numbers and the function I suggested are not equivalent!

Sergio Losilla
  • 730
  • 1
  • 5
  • 14
0

This is an old post, but what you are asking for is decimal precision with binary mathematics. The conversion between the two is giving you an apparent distinction.

The main point, I think, which you are making is to do with identity, so that you can use equality/inequality comparisons between two numbers.

Because of the fact that there is a discrepancy between what we humans use (decimal) and what computers use (binary), we have three choices.

  1. We use a decimal library. This is computationally costly, because we are using maths which are different to how computers work. There are several, and one day they may be adopted into std. See eg "ISO/IEC JTC1 SC22 WG21 N2849"

  2. We learn to do our maths in binary. This is mentally costly, because it's not how we do our maths normally.

  3. We change our algorithm to include an identity test.

  4. We change our algorithm to use a difference test.

With option 3, it is where we make a decision as to just how close one number needs to be to another number to be considered 'the same number'.

One simple way of doing this is (as given by @SirGuy above) where we use ceiling or floor as a test - this is good, because it allows us to choose the significant number of digits we are interested in. It is domain specific, and the solution that he gives might be a bit more optimal if using a power of 2 rather than of 10.

You definitely would only want to do the calculation when using equality/inequality tests.

So now, our equality test would be (for 10 binary places (nearly 3dp))

// Normal identity test for floats.
// Quick but fails eg 1.0000023 == 1.0000024 
return (a == b);

Becomes (with 2^10 = 1024).

// Modified identity test for floats.
// Works with 1.0000023 == 1.0000024 
return (std::floor(a * 1024) == std::floor(b * 1024));

But this isn't great I would go for option 4. Say you consider any difference less than 0.001 to be insignificant, such that 1.00012 = 1.00011.

This does an additional subtraction and a sign removal, which is far cheaper (and more reliable) than bit shifts.

// Modified equality test for floats.
// Returns true if the ∂ is less than 1/10000.
// Works with 1.0000023 == 1.0000024 
return abs(a - b) < 0.0001;

This boils down to your comment about calculating circularity, I am suggesting that you calculate the delta (difference) between two circles, rather than testing for equivalence. But that isn't exactly what you asked in the question...

Konchog
  • 1,920
  • 19
  • 23