[英]How to check for floating point precision in Double
以这个简单的功能为例。
int checkIfTriangleIsValid(double a, double b, double c) {
//fix the precision problem
int c1, c2, c3;
c1 = a+b>c ? 0 : 1;
c2 = b+c>a ? 0 : 1;
c3 = c+a>b ? 0 : 1;
if(c1 == 0 && c2 == 0 && c3 == 0)
return 0;
else {
printf("%d, %d, %d\n",c1, c2, c3);
return 1;
}
}
我放置a = 1.923
, b = 59.240
, c = 61.163
现在由于某种原因,当我检查c1中的条件时,它应该给我1,但是,它却给我0。我尝试使用%.30f
进行printf
,发现以后值会更改。
我该如何解决这个问题?
编辑:我检查了其他类似我的问题,但他们甚至没有双。
您的C实现可能使用double
的IEEE-754基本64位二进制浮点格式。 当1.923
, 59.240
,和61.163
被正确地转换为可表示在最近的值double
,结果是完全:
如您所见,其中的前两个总和大于第三个。 这意味着,当您将这些值分配给double
对象时,它们已经以改变其关系的方式进行了更改。 由于原始信息已消失,因此后续计算无法修复。
由于转换为double
后没有解决方案可以工作,因此您需要一个在转换为double
之前或代替其运行的解决方案。 如果要使用值1.923、59.240和61.163精确地或更精确地计算,则可能需要编写自己的十进制算术代码或查找其他支持十进制算术的代码。 如果只想使用小数点后三位的数字,则可能的解决方案是编写一些读取输入的代码,例如“ 59.240”,然后将其返回到以1000缩放的整数对象中,从而返回59240。 然后可以容易地测试所得值的三角形不等式。
当我检查c1中的条件时,应该给我1,但是给我0
我该如何解决这个问题?
改变您的期望。
一个典型的double
可以精确表示大约2 64个不同的值。 1.923、59.240、61.163通常不在该集合中,因为double
通常以二进制方式进行编码。 例如binary64 。
当为a,b,c
分配1.923、59.240、61.163时,它们获得的值更像下面的值,即壁橱double
。
a 1.923000000000000042632564145606...
b 59.240000000000001989519660128281...
c 61.162999999999996703081706073135...
在我的情况下, a
和b
都收到比十进制代码形式稍高的值,而c
收到略低于十进制代码形式的值。
加a+b
,总和四舍五入,远离c
。
printf("a+b %35.30f\n", a+b);
a+b 61.163000000000003808509063674137
a + b > c
以及其他比较和OP均为真
checkIfTriangleIsValid(1.923, 59.240, 61.163)
应该返回有效 (0),因为它实际上更像是checkIfTriangleIsValid(1.9230000000000000426..., 59.24000000000000198..., 61.16299999999999670...)
加a+b
更加复杂,因为加法可能使用double
或long double
数学进行。 研究FLT_EVAL_METHOD
了解详细信息。 取整模式也会影响最终的总和。
#include <float.h>
printf("FLT_EVAL_METHOD %d\n", FLT_EVAL_METHOD);
对于替代的三角检查,请减去最大的2个值,然后与最小的值进行比较。
a > (cb)
可以比(a+b) > c
保留更高的精度。
// Assume a,b,c >= 0
int checkIfTriangleIsValid_2(double a, double b, double c) {
// Sort so `c` is largest, then b, a.
if (c < b) {
double t = b; b = c; c = t;
}
if (c < a) {
double t = a; a = c; c = t;
}
if (a > b) {
double t = b; b = a; a = t;
}
// So far, no loss of precision is expected due to compares/swaps.
// Only now need to check a + b >= c for valid triangle
// To preserve precision, subtract from `c` the value closest to it (`b`).
return a > (c-b);
}
如果时间允许,我将在以后进行更多审查。 这种方法对准确的答案很有帮助-但需要评估更多的极端情况。 它报告一个有效的三角形checkIfTriangleIsValid_2(1.923, 59.240, 61.163))
。
FLT_EVAL_METHOD
,舍入模式和double
编码可能在其他平台上导致不同的答案。
笔记:
出现一个checkIfTriangleIsValid()
返回0表示有效三角形 。
当三角形的面积为0,预期结果为1或invalid时,它也会出现。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.