0

i update my code for this :

#include <stdio.h>
int IsRightTriangle(float x,float y,float z )
{
    
    int result;
    
    if( ((x*x)+(y*y)-(z*z)>0.999 && (x*x)+(y*y)-(z*z)<1)  || ((x*x)+(z*z)-(y*y)>0.999 && (x*x)+(z*z)-(y*y)<1) || ((y*y)+(z*z)-(x*x)>0.999 &&(y*y)+(z*z)-(x*x)<1)) {
      
        result = 1; 
        return  result ;      
    } else { 
        result =0 ;
        return result;
    }
}

-but still have the same problem with decimals -for example : Running test: IsRightTriangle(edge1=15.26, edge2=8.00, edge3=13.00) -- Failed

I'm trying to write a code that checks if a triangle is right (using decimals values).

That's my code, and the problem is that it always rounds off the float value. What can I change in it?

int IsRightTriangle(float x, float y, float z)
{
    int result;
    
    if((x*x) + (y*y) == (z*z) || (x*x) + (z*z) == (y*y) || (y*y) + (z*z) == (x*x)) {
        result = 1; 
        return result ;   
    }
    else { 
        result =0 ;
        return result;
    }
}
30
  • 1
    As a solution... You need to see if values are close enough. They'll almost never be equal as you noted. What is close enough, depends. Commented Jan 17, 2023 at 12:10
  • 1
    Also good to read: stackoverflow.com/questions/588004/… Commented Jan 17, 2023 at 12:13
  • 1
    There are only a certain number of bits in a float. You can imagine the problem in decimal if it's easier: if(x + y + z == 1) does 1/3 + 1/3 + 1/3 == 1 ? No, not according to the computer, because 1/3 is 0.3333333 with only a certain number of digits and it adds up to 0.9999999, not 1. Commented Jan 17, 2023 at 12:21
  • 2
    @proran I just tried your code, and it seems to work fine for me. It told me that 3,4,5 and 5,12,13 were right triangles, and that others were not. Can you say a little more about this "roundoff" problem you're having? Commented Jan 17, 2023 at 12:43
  • 2
    @proran Be aware that 15.26 is only part of the problem. The square root of 233 is not 15.26. But it is not 15.2643375, either. So you are never going to be able to perfectly test the triangle 8, 13, √233. A certain amount of, as you called it, roundoff is inevitable here, before you even get to your IsRightTriangle function. You may choose to say that 15.2643375 is "close enough", and then you have to decide how close is "close enough" when asking whether x*x + y*y is equal to z*z. (And you're almost there.) Commented Jan 17, 2023 at 14:39

3 Answers 3

1

You can't avoid rounding. I repeat: you cannot avoid it. It is not possible.

There are only a certain number of bits in a float.

Here's the decimal version of the problem if we pretend that floats are in decimal instead of binary:
When I write if(x + y + z == 1) does 1/3 + 1/3 + 1/3 == 1 ? No, not according to the computer, because 1/3 is 0.3333333 with only a certain number of digits and it adds up to 0.9999999, not 1.

It is quite common to just add some "tolerance range" as a quick workaround. Instead of if(x + y + z == 1) you might write if(x + y + z > 0.9999 && x + y + z < 1.0001) and this is often good enough, especially for computer games. How many 9's and 0's do you need? Well, just write a few and then play the game and see if it feels right.

For some applications this might not be suitable, and then you have to invent a completely different way to do whatever you are trying to do. For example, you might store all your numbers as rational numbers (numerator and denominator, a.k.a. fractions) instead of floating-point. Rational numbers can be calculated exactly - if the numerator and denominator don't overflow. C doesn't have rational numbers built in, so you would need to write your own library functions like struct rational, void rational_add(struct rational *a, struct rational *b) and so on.

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

20 Comments

The answer is wrong. Maybe if you had said “It is very difficult to avoid rounding using only float arithmetic,” you would have been off to a better start. But “It is not possible” is false. It is possible to do exact arithmetic with floating-point arithmetic, using sufficient care. There are textbooks written about it. And, in this case, the solution might be quite simple, using double arithmetic to work with the float inputs. But a solution is possible even using just float, although it would require a fair amount of work.
@user253751 You have to be careful with the analogy about 1/3 + 1/3 + 1/3. It's a nice analogy, but don't say "not according to the computer", because if you do 1./3 + 1./3 + 1./3 on any computer I know of, you do get exactly 1.000.
@user253751: Computing 1.f/3 + 1.f/3 + 1.f/3 using IEEE-754 binary32 arithmetic for float yields exactly 1, not 1.0000000298. Computing 1./3 + 1./3 + 1./3 using IEEE-754 binary64 arithmetic for double also yields exactly 1. If you compute 1.f/3 using float arithmetic, then convert that to double, then do the two additions using double, then you get 1.0000000298023223876953125. But this question is tagged C, and C expression evaluation does not do that without some intervention by the programmer using casts or assignments to change the type.
@EricPostpischil double might be able to accurately represent the result of any float*float multiplication (floats which have already been rounded, mind you, so it's too late) but if that's your objection, just imagine the original question had double inputs and the question remains.
@EricPostpischil Can you give some good references that discuss that in more details? I've never thought it was possible.
|
1

There are two issues here, and the answer to neither of them is that you want to try to somehow avoid doing any rounding. In fact, you're probably going to need to do some well-chosen rounding.

The first issue is that no finite-precision floating-point representation can represent every fraction exactly. It's especially true that it's impossible to represent irrational numbers like sqrt(233) exactly.

You tried to test your program on a triangle with sides 8, 13, and the square root of 233. Mathematically, that's a perfect right triangle, but it's impossible to ask your program to test that right triangle, because you literally can't say "the square root of 233" when you ask it. You certainly can't say 155.26. You can try 15.26434, but that's inaccurate, as is 15.264337522473748, or 15.2643375224737480252559487. No finite representation is ever going to be perfectly accurate.

And then the second problem is that the inherent imprecision in the representation of most fractions means that you're rarely going to find that, say, x*x + y*y is exactly equal to z*z. This is an example of a comparison for floating-point equality that you probably don't want to try to make. (You will often hear it said that you should "never compare floating-point numbers for equality", which is not too bad as a rule of thumb, but there's a lot more to be said.)

You tried updating your comparison code to

if(x*x + y*y - z*z > 0.999 && x*x + y*y - z*z < 1 || … )

but that's not quite right, either. If the quantities x*x + y*y and z*z are almost equal, their difference will be close to 0, although it could end up on either side. So what you were trying to do is more like

if(x*x + y*y - z*z > -.001 && x*x + y*y - z*z < 0.001 || … )

and this might actually work, to a point. You could simplify it (avoid the repeated subexpression) by writing

if(fabs(x*x + y*y - z*z) < 0.001 || … )

Using a fixed accuracy threshold like 0.001 like this doesn't work particularly well, however. Some better, relative approaches can be found in this question's other answers, or in question 14.5 in the C FAQ list.

Also, as another rule of thumb, you should almost never use type float. You should almost always use type double, which has roughly twice the precision, and will tend to give you far fewer headaches.

Comments

0

To compare floating point values, you should compare equality with a given tolerance. Unless the scale of the numbers are known, it is important to apply the tolerance to a "normalized" expression of the difference between the numbers.

For example, fabs(a-b) < tolerance only gives the desired result if a and b are close to 1. A better alternative is fabs(a/b)-1.0, however, that assumes that b is not 0 or very close to 0. In this case, I assume that the operands are not zero (or close to zero), and thus a solution is as follows:

#include <stdbool.h>
#include <math.h>

bool IsRightTriangle( float x, float y, float  z)
 {
    float tolerance = 0.0001;
    int result;
    
    return (fabs((x*x + y*y) / (z*z) - 1.0) < tolerance ||
        fabs((x*x + z*z) / (y*y) - 1.0) < tolerance ||
        fabs((y*y + z*z) / (x*x) - 1.0) < tolerance);
}

5 Comments

This is not what the question requests, and it is not good general advice for working with floating-point arithmetic. While this might eliminate false negatives, where the function incorrectly reports that values that do form a right triangle do not form a right triangle, it increases false positives, where the function incorrectly reports that values that do not form a right triangle do form a right triangle. It is not always desired to make this trade-off, which is one reason this is not good general advice.
i update my code for this :#include <stdio.h> int IsRightTriangle(float x,float y,float z ) { int result; if( ((xx)+(yy)-(zz)>0.999 && (xx)+(yy)-(zz)<1) || ((xx)+(zz)-(yy)>0.999 && (xx)+(zz)-(yy)<1) || ((yy)+(zz)-(xx)>0.999 &&(yy)+(zz)-(xx)<1)){ result = 1; return result ; } else { result =0 ; return result; } } and still have a problem , for example : Running test: IsRightTriangle(edge1=15.26, edge2=8.00, edge3=13.00) -- Failed
my code failed agian with : IsRightTriangle(edge1=35.61, edge2=22.00, edge3=28.00) -- Failed
@proran Do you expect these examples to give "true" or "false" as result? With a tolerance of 0.0001 the first gives false and the second gives true on my system. They are both not very close to being exact triangles. Finding the right tolerance for the application may not be easy.
@proran By the way, compared to your updated code, notice that I divide the values to be compared (to mitigate scaling issues) and take the absolute value of the difference (to allow for tolerance in both directions).

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.