浮点运算
Floating point Arithmetics
今天在我的 C++ 编程课上,我的教授告诉我,永远不要直接比较两个浮点值。
于是我尝试了这段代码,找出了他的说法的原因。
double l_Value=94.9;
print("%.20lf",l_Value);
我发现结果为 94.89999999(一些相对错误)
我了解浮点数的存储方式与将其呈现给代码的方式不同。以二进制形式压缩那些 1 和 0 涉及一些相对舍入误差。
我正在寻找两个问题的解决方案。
1. 比较两个浮点值的有效方法。
2.如何将一个浮动值添加到另一个。例子。将 0.1111 添加到 94.4345 以获得精确值 94.5456
提前致谢。
我通常这样做的方式是自定义相等比较函数。基本思想是你有一定的容忍度,比如 0.0001 之类的。然后你减去你的两个数字并取它们的绝对值,如果它小于你的容忍度,你就把它当作相等的。当然,还有其他一些策略可能更适合某些情况。
为自己定义一个公差级别e
(例如e=.0001
)并检查是否abs(a-b) <= e
您不会得到 "exact" 浮点值。曾经。如果您事先知道您使用的是四位小数,并且您想要 "exact",那么您需要在内部将您的数字视为整数,并且只将它们显示为小数。 944345 + 1111 = 945456
- Efficient way to compare two floating values.
一个简单的double a,b; if (a == b)
是比较两个浮点值的有效方法。然而,正如 OP 所注意到的,这可能不符合总体编码目标。更好的方法取决于比较的上下文,OP 没有提供。请看下面。
- How to add a floating value to another one. Example. Add 0.1111 to 94.4345 to get the exact value as 94.5456
作为源代码的浮点值具有有效的无限范围和精度,例如1.23456789012345678901234567890e1234567
。将此文本转换为 double
通常限于 264 个不同值之一。选择最接近的,但可能不完全匹配。
0.1111, 94.4345, 94.5456
都不能 完全 作为典型的 double
。
OP有选择:
1.) 使用 double, float
以外的其他类型。各种库提供十进制浮点类型。
2) 将支持 double
的稀有平台的代码限制为以 10 为基数的形式,以便 FLT_RADIX == 10
.
3) 编写您自己的代码来处理 "0.1111"
等用户输入到 structure/string 中并执行所需的操作。
4) 将用户输入视为 字符串 并转换为某种整数类型,再次使用支持的例程 read/compute/and 写入。
5) 接受浮点运算在数学上不精确并处理舍入误差。
double a = 0.1111;
printf("a: %.*e\n", DBL_DECIMAL_DIG -1 , a);
double b = 94.4345;
printf("b: %.*e\n", DBL_DECIMAL_DIG -1 , b);
double sum = a + b;
printf("sum: %.*e\n", DBL_DECIMAL_DIG -1 , sum);
printf("%.4f\n", sum);
输出
a: 1.1110000000000000e-01
b: 9.4434500000000000e+01
sum: 9.4545599999999993e+01
94.5456 // Desired textual output based on a rounded `sum` to the nearest 0.0001
More on #1
如果不寻求精确比较,而是寻求某种"are the two values close enough?",则需要"close enough"的定义——其中有很多。
下面"close enough"通过考察两个数的ULP来比较距离。当值是相同的二次方时,它是线性差异,否则变为对数。当然,换号是个问题。
float
例子:
考虑从最负到最正排序的所有有限 float
。以下是可移植的代码,returns 每个 float
的整数,具有 相同的顺序 。
uint32_t sequence_f(float x) {
union {
float f;
uint32_t u32;
} u;
assert(sizeof(float) == sizeof(uint32_t));
u.f = x;
if (u.u32 & 0x80000000) {
u.u32 ^= 0x80000000;
return 0x80000000 - u.u32;
}
return u.u3
}
现在,要确定两个 float
是否是 "close enough",只需比较两个整数。
static bool close_enough(float x, float y, uint32_t ULP_delta) {
uint32_t ullx = sequence_f(x);
uint32_t ully = sequence_f(y);
if (ullx > ully) return (ullx - ully) <= ULP_delta;
return (ully - ullx) <= ULP_delta;
}
今天在我的 C++ 编程课上,我的教授告诉我,永远不要直接比较两个浮点值。
于是我尝试了这段代码,找出了他的说法的原因。
double l_Value=94.9;
print("%.20lf",l_Value);
我发现结果为 94.89999999(一些相对错误)
我了解浮点数的存储方式与将其呈现给代码的方式不同。以二进制形式压缩那些 1 和 0 涉及一些相对舍入误差。
我正在寻找两个问题的解决方案。 1. 比较两个浮点值的有效方法。 2.如何将一个浮动值添加到另一个。例子。将 0.1111 添加到 94.4345 以获得精确值 94.5456
提前致谢。
我通常这样做的方式是自定义相等比较函数。基本思想是你有一定的容忍度,比如 0.0001 之类的。然后你减去你的两个数字并取它们的绝对值,如果它小于你的容忍度,你就把它当作相等的。当然,还有其他一些策略可能更适合某些情况。
为自己定义一个公差级别
e
(例如e=.0001
)并检查是否abs(a-b) <= e
您不会得到 "exact" 浮点值。曾经。如果您事先知道您使用的是四位小数,并且您想要 "exact",那么您需要在内部将您的数字视为整数,并且只将它们显示为小数。 944345 + 1111 = 945456
- Efficient way to compare two floating values.
一个简单的double a,b; if (a == b)
是比较两个浮点值的有效方法。然而,正如 OP 所注意到的,这可能不符合总体编码目标。更好的方法取决于比较的上下文,OP 没有提供。请看下面。
- How to add a floating value to another one. Example. Add 0.1111 to 94.4345 to get the exact value as 94.5456
作为源代码的浮点值具有有效的无限范围和精度,例如1.23456789012345678901234567890e1234567
。将此文本转换为 double
通常限于 264 个不同值之一。选择最接近的,但可能不完全匹配。
0.1111, 94.4345, 94.5456
都不能 完全 作为典型的 double
。
OP有选择:
1.) 使用 double, float
以外的其他类型。各种库提供十进制浮点类型。
2) 将支持 double
的稀有平台的代码限制为以 10 为基数的形式,以便 FLT_RADIX == 10
.
3) 编写您自己的代码来处理 "0.1111"
等用户输入到 structure/string 中并执行所需的操作。
4) 将用户输入视为 字符串 并转换为某种整数类型,再次使用支持的例程 read/compute/and 写入。
5) 接受浮点运算在数学上不精确并处理舍入误差。
double a = 0.1111;
printf("a: %.*e\n", DBL_DECIMAL_DIG -1 , a);
double b = 94.4345;
printf("b: %.*e\n", DBL_DECIMAL_DIG -1 , b);
double sum = a + b;
printf("sum: %.*e\n", DBL_DECIMAL_DIG -1 , sum);
printf("%.4f\n", sum);
输出
a: 1.1110000000000000e-01
b: 9.4434500000000000e+01
sum: 9.4545599999999993e+01
94.5456 // Desired textual output based on a rounded `sum` to the nearest 0.0001
More on #1
如果不寻求精确比较,而是寻求某种"are the two values close enough?",则需要"close enough"的定义——其中有很多。
下面"close enough"通过考察两个数的ULP来比较距离。当值是相同的二次方时,它是线性差异,否则变为对数。当然,换号是个问题。
float
例子:
考虑从最负到最正排序的所有有限 float
。以下是可移植的代码,returns 每个 float
的整数,具有 相同的顺序 。
uint32_t sequence_f(float x) {
union {
float f;
uint32_t u32;
} u;
assert(sizeof(float) == sizeof(uint32_t));
u.f = x;
if (u.u32 & 0x80000000) {
u.u32 ^= 0x80000000;
return 0x80000000 - u.u32;
}
return u.u3
}
现在,要确定两个 float
是否是 "close enough",只需比较两个整数。
static bool close_enough(float x, float y, uint32_t ULP_delta) {
uint32_t ullx = sequence_f(x);
uint32_t ully = sequence_f(y);
if (ullx > ully) return (ullx - ully) <= ULP_delta;
return (ully - ullx) <= ULP_delta;
}