0

This issue fundementally comes down to 2 lines, the 1st line will output 1.

std::cout << sqrt(pow(b->x - a->x, 2) + pow(b->y - a->y, 2) + pow(b->z - a->z, 2)) << std::endl;

Yet the 2nd line will output 0, how is this possible?

std::cout << (sqrt(pow(b->x - a->x, 2) + pow(b->y - a->y, 2) + pow(b->z - a->z, 2)) == 1) << std::endl;

Minimal complete verifiable example:

struct vertice {
    double x, y, z;
    vertice(double x, double y, double z) {
        this->x = x;
        this->y = y;
        this->z = z;
    }
};

void cartDistance(const vertice * a, const vertice * b);

int main() {
    cartDistance(new vertice(0, 0, 0), new vertice(0, 0, 1));
    system("pause");
    return 0;
}

void cartDistance(const vertice * a, const vertice * b) {
    std::cout << "dist: " << sqrt(pow(b->x - a->x, 2) + pow(b->y - a->y, 2) + pow(b->z - a->z, 2)) << std::endl;
    std::cout << "dist check: " << (sqrt(pow(b->x - a->x, 2) + pow(b->y - a->y, 2) + pow(b->z - a->z, 2)) == 1) << std::endl;
}
Jonathan Woollett-light
  • 2,813
  • 5
  • 30
  • 58

1 Answers1

2

The stream output operator is rounding because you don't really want to see 0.999999999999999999997. The math is inexact, and that is being hidden from you. The comparison operator does care about this, though.

Floating point operations require care.

Using std::setprecision () will allow you to see more digits after the decimal, at which point your problem will be more obvious.

Richard
  • 56,349
  • 34
  • 180
  • 251
  • I already suspected this, my question is effectively why is this happening it seems absurd to store data as such, and how does one deal with this problem. – Jonathan Woollett-light Jan 24 '18 at 14:35
  • 1
    @JonathanWoollett-light For example, if you require exact maths for something like currency then you hold cents rather then dollars in an integer variable of the appropriate size. Otherwise you need to deal with the fact that there are not enough bits in the universe to exactly represent number like 0.1 (base10) in base2 floating point. – Richard Critten Jan 24 '18 at 14:37
  • Do you know any resources explaining why this occurs? From my knowledge of floating point values using mantissa and exponent it just doesn't seem reasonable that it cannot simply store 1 as 1 rather than some approximation. – Jonathan Woollett-light Jan 24 '18 at 14:39
  • 1
    The value `0.1` is irrational in binary. Multiplying it by 10 does not give you one because of this. How would you design a storage system to hold an infinite number of numbers in a finite space? The absurdity you see is a well-considered and intentional engineering decision upon which we have, literally, built the world you live in. – Richard Jan 24 '18 at 14:39
  • floats use binary fractions 1/2; 1/4; 1/8; 1/16 etc so in decimal they are 0.5, 0.25, 0.125; 0.0625 etc have a play trying to make 0.1 out of the above fractions. Link to info: https://randomascii.wordpress.com/category/floating-point/ In summary `0.1f * 10.0f != 1` it will be very close though. – Richard Critten Jan 24 '18 at 14:41
  • @Jonathan: It can store 1. The problem is that if you ever do math on the 1, which you do here, it is very probably no longer exactly 1. Just very close. One typically does math to get a variable `a` and then asks if `std::abs (a-1)<1e-6` or some such. That is, you see if something is close enough. – Richard Jan 24 '18 at 14:42
  • 2
    @Richard: .1 is not irrational. Irrationality is a property of numbers; it is independent of any base used to represent them. You mean .1 is not exactly representable in a finite binary format. – Eric Postpischil Jan 24 '18 at 15:01
  • @RichardCritten Well, [it *can* be](https://ideone.com/Crih7r) `0.1f * 10.0f == 1`. Implementations are allowed to use a greater precision for intermediate values. Still, for floating point values, it is always better to check if two values are *almost* equal. – Bob__ Jan 24 '18 at 15:22
  • Thanks, @EricPostpischil. This was what I meant. – Richard Jan 24 '18 at 16:08