我想知道将浮点数比较到三位小数的最快方法。我有类似的东西
float lhs = 2.567xxxx
float rhs = 2.566xxxx
上面应该是不同的,如果它是这样的
float lhs = 2.566xxxx
float rhs = 2.566xxxx
它们应该是相同的
更新
我正在尝试以下
double trunc(double d)
{
return (d>0) ? floor(d) : ceil(d) ;
}
bool comparedigits(float a , float b)
{
if (trunc(1000.0 * a) == trunc(1000.0 * b))
{
return true;
}
return false;
}
float g = 2.346;
float h= 2.34599;
bool t = comparedigits(g,h) ; //Not the same and should return false;
然而它正在回归真实。
答案 0 :(得分:9)
为了阻止错误的答案的冲击,因为它们允许舍入来改变结果,这里是一个没有舍入问题的答案,因为它使用double
进行算术:
trunc(1000. * lhs) == trunc(1000. * rhs);
这是有效的,因为1000.
的类型为double
,因此另一个操作数从float
转换为double
,并且乘法在double
中执行格式。具有任何float
值的1000的乘积在double
中是完全可表示的,因此没有舍入误差(假设IEEE 754 32位和64位二进制浮点)。然后我们使用trunc
来比较小数点后的(原始)第三位数字。
我犹豫是否提供这个答案,因为我不确定这是OP真正想要的。通常当人们向Stack Overflow提出比较“到三位小数”的请求时,他们并没有完全考虑到这个问题。一个完整正确的答案可能要等到我们澄清。
此外,以上仅适用于正数。如果值可能为负,则应对其符号执行先前测试,如果它们不同,则应返回false
。 (否则,-。0009将被报告为等于+.0009。)
答案 1 :(得分:6)
对于可以在x1000
之后放入整数的浮点值,您可以尝试:
if (static_cast<int>(lhs*1000.0) == static_cast<int>(rhs*1000.0))
{
// Values are near
}
else
{
// They are not identical (maybe!)
}
在表示浮动值时要小心计算机的准确性。
重要更新
始终存在可能导致代码失败的数字,Eric Postpischil的代码与此代码的代码相同。
即使转换为字符串也无济于事,我们可以找到无法正确转换为字符串的数字。
嗯,解决方案是什么?这很容易,我们必须定义我们程序的范围和所需的准确性。我们不能在计算机世界中拥有无限的精确度。 What Every Computer Scientist Should Know About Floating-Point Arithmetic
答案 2 :(得分:2)
如果我们假设你的陈述中的xxxx
是真的不关心,那就是你只关心7位小数的精度,那么下面的方案就可以了。
由于float
的精度有限,要处理浮点表示效果,可以将参数提升为double,舍入到7 th 小数位,然后乘以1000。然后,您可以使用modf()
提取整数部分并进行比较。
bool equals_by_3_decimal_places (float a, float b) {
double ai, bi;
modf((.00000005 + a) * 1000, &ai);
modf((.00000005 + b) * 1000, &bi);
return ai == bi;
}
答案 3 :(得分:1)
将float
值转换为具有完整位数(std::numeric_limits<float>::dgits10
)的字符串,然后将字符串截断为3位小数,并比较生成的字符串:
std::string convert(float value, int places) {
if (value == 0) {
return "0";
}
int digits(std::numeric_limits<float>::digits10 - std::log(value) / std::log(10));
digits = std::max(0, digits);
std::ostringstream out;
out << std::fixed << std::setprecision(digits) << value;
std::string rc(out.str());
return places < digits? rc.substr(0, rc.size() - (digits - places)): rc;
}
bool compare(float f1, float f2) {
return convert(f1, 3) == convert(f2, 3);
}
所提出的各种比较乘以100或1000不起作用,因为它们将进行二进制而不是十进制舍入。您可以尝试在乘法之后和截断到int
之前添加0.5,但是有些情况(尽管很少)这种方法仍然失败。但是,只要您的结果不超过std::numeric_limits<float>::digit10
位数,上述转换就是正确的。尝试处理比此数字更多的十进制数字将失败,因为float
无法正确表示正确的十进制数字。
答案 4 :(得分:0)
1)你试图用浮点数做等于比较。一些浮点格式可以工作,但IEEE格式不起作用。你不能做等于比较。您需要将该float转换为int然后执行int比较。使用整数(不限于32位或此处的任何内容),只有一种方法可以表示每个数字,因此您可以进行等于比较。
2)记住浮点数学是基数2,你要求做基数为10的东西。所以会有转换问题,截断。此外,我再次假设你使用IEEE,这意味着你有三种舍入模式(基数2),所以你也必须处理它。你会想做一些double_to_integer((double * 1000.0)+0.5)并比较那些。如果您发现不起作用的角落情况,我不会感到惊讶。
有关此问题的更多有趣信息。请注意,C标准不支持以这种方式使用联合,但通常恰好可以工作......
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
double trunc(double d)
{
return (d>0) ? floor(d) : ceil(d) ;
}
int comparedigits(float a , float b)
{
if (trunc(1000.0 * a) == trunc(1000.0 * b))
{
return 1;
}
return 0;
}
union
{
unsigned int ul;
float f;
} fun;
union
{
unsigned int ul[2];
double d;
} dun;
int main ( void )
{
float g;
float h;
int t;
g = 2.346;
h = 2.34599;
t = comparedigits(g,h);
printf("%u\n",t);
printf("raw\n");
fun.f=g; printf("0x%08X\n",fun.ul);
fun.f=h; printf("0x%08X\n",fun.ul);
dun.d=g; printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
dun.d=h; printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
printf("trunc\n");
dun.d=trunc(1000.0 * g); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
dun.d=trunc(1000.0 * h); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
printf("trunc\n");
dun.d=trunc(1000.0F * g); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
dun.d=trunc(1000.0F * h); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
printf("floor\n");
dun.d=floor(1000.0 * g); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
dun.d=floor(1000.0 * h); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
printf("ceil\n");
dun.d=ceil(1000.0 * g); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
dun.d=ceil(1000.0 * h); printf("0x%08X_%08X\n",dun.ul[1],dun.ul[0]);
printf("%u\n",(unsigned int)(g*1000.0));
printf("%u\n",(unsigned int)(h*1000.0));
if (trunc(1000.0F * g) == trunc(1000.0F * h))
{
printf("true\n");
}
else
{
printf("false\n");
}
return(0);
}
编译并运行
gcc test.c -o test -lm
./test
1
raw
0x401624DD
0x401624B3
0x4002C49B_A0000000
0x4002C496_60000000
trunc
0x40A25200_00000000
0x40A25200_00000000
trunc
0x40A25400_00000000
0x40A25200_00000000
floor
0x40A25200_00000000
0x40A25200_00000000
ceil
0x40A25400_00000000
0x40A25400_00000000
2345
2345
false
所以在单个数学而不是双重数学中进行1000 * x似乎可以解决问题
1000.0 * a是混合模式。除非指定为单一,否则1000.0是C标准的双倍。并且a是单个,所以a被转换为double,数学运算为double,然后将其转换为double函数。 1000.0F是单个,a是单个,所以乘法是单个数学,然后它被转换为double。所以也许真正的问题在于将g和h转换和舍入为double。将不得不深入挖掘尾数差异......
我认为关键是这个,双倍单次1000.0 * x结果
trunc
0x40A25200_00000000
0x40A25200_00000000
如果它们相等,那么你做同样数字的任何东西都会相同。当它是单次然后转换为双倍时,它们会有所不同。
trunc
0x40A25400_00000000
0x40A25200_00000000
这使您的代码工作(对于这两个特定值)。
false