简体   繁体   English

如何避免对 C 中的值进行四舍五入?

[英]How can I avoid rounding off values in C?

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 - 但仍然存在与小数相同的问题 - 例如:运行测试:IsRightTriangle(edge1=15.26, edge2=8.00, edge3=13.00) -- 失败

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;
    }
}

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?当我写if(x + y + z == 1)时,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.不,不是根据计算机,因为 1/3 是 0.3333333 只有一定位数,它加起来是 0.9999999,而不是 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.你可以写if(x + y + z > 0.9999 && x + y + z < 1.0001)而不是if(x + y + z == 1) ,这通常就足够了,尤其是对于电脑游戏。 How many 9's and 0's do you need?你需要多少个 9 和 0? 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, aka 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. C 没有内置有理数,因此您需要编写自己的库函数,如struct rationalvoid rational_add(struct rational *a, struct rational *b)等。

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.尤其是不可能准确地表示像sqrt(233)这样的无理数。

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.您尝试在边长为 8、13 和平方根为 233 的三角形上测试您的程序。从数学上讲,这是一个完美的直角三角形,但不可能让您的程序测试该直角三角形,因为您确实不能说当你问它时,“233 的平方根”。 You certainly can't say 155.26.你当然不能说 155.26。 You can try 15.26434, but that's inaccurate, as is 15.264337522473748, or 15.2643375224737480252559487.您可以尝试使用 15.26434,但这是不准确的,例如 15.264337522473748 或 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 .然后第二个问题是大多数分数的表示中固有的不精确性意味着您很少会发现x*x + y*y正好等于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.如果x*x + y*yz*z几乎相等,则它们的差值将接近于 0,尽管它可能最终落在任何一边。 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.但是,像这样使用固定精度阈值(如0.001 )并不是特别好。 Some better, relative approaches can be found in this question's other answers, or in question 14.5 in the C FAQ list .可以在该问题的其他答案或C FAQ 列表中的问题 14.5中找到一些更好的相关方法。

Also, as another rule of thumb, you should almost never use type float .另外,作为另一个经验法则,你几乎不应该使用float类型。 You should almost always use type double , which has roughly twice the precision, and will tend to give you far fewer headaches.您应该几乎总是使用double类型,它的精度大约是它的两倍,而且往往会给您带来更少的麻烦。

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(ab) < 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:例如, fabs(ab) < tolerance仅在ab接近 1 时给出所需的结果。更好的选择是fabs(a/b)-1.0 ,但是,它假定b不为 0 或非常接近 0 . 在这种情况下,我假设操作数不为零(或接近零),因此解决方案如下:

#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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM