float f = 0.7; if( f == 0.7 ) printf("equal"); else printf("not equal");
为什么输出是not equal?为什么会发生这种情况?
not equal
gev0vcfq1#
这是因为在你的陈述中
if(f == 0.7)
0.7被视为双精度浮点数。尝试使用0.7f以确保该值被视为浮点数:
if(f == 0.7f)
但正如Michael在下面的注解中所建议的,永远不要测试浮点值是否完全相等。
webghufk2#
这个答案补充了现有的答案:注意0.7不能精确地表示为浮点数(或双精度数)。如果它被精确地表示,那么在转换为浮点数然后再转换回双精度数时就不会丢失信息,也就不会有这个问题。甚至可以说,对于不能精确表示的文字浮点常量,应该有一个编译器警告,特别是当标准对于舍入是在运行时以当时设置的模式进行,还是在编译时以另一种舍入模式进行时,是如此模糊时。所有可以精确表示的非整数都以5作为最后一位十进制数字。不幸的是,匡威就不成立了:有些数的最后一个十进制数字是5,因此无法精确表示。2小整数都可以精确表示,只要不进入反规格化数的领域,除以2的幂就可以将一个可以表示的数转换为另一个可以表示的数。
5
nbysray53#
首先,让我们看看浮点数的内部。我取0.1f,它是4字节长(二进制32),十六进制是
按标准IEEE 754把它转换成十进制我们必须这样做:
在二进制3D CC中,CC CD为
这里第一个数字是符号位。0表示(-1)^0,表示我们的数是正数。第二个8位是一个指数。二进制为01111011,十进制为123。但实际的指数是123 - 127(总是127)=-4,这意味着我们需要将得到的数字乘以2 ^(-4)。最后23个字节是有效位精度。第一位乘以1/(2^1)(0.5),第二位乘以1/(2^2)(0.25),依此类推。下面是我们得到的结果:第一次我们需要把所有的数字(2的幂)相加,然后再加上1(通常都是1)。1,60000002384185791015625现在我们把这个数乘以2 ^(-4),它来自指数。我们只是把上面的数除以2四次:
我使用MS计算器
**
第二次我取数字0.1因为没有整数部分。第一个符号位-它是0。指数和有效位精度,我现在将计算。逻辑是乘以2整数(0.1 * 2 = 0.2),如果它大于1,减去并继续。
数字是.0001100110011001100110011001100110011,标准表示在得到1之前我们必须左移。你看我们需要4次移位,从这个数字计算指数(127 - 4 =123)。现在的有效位精度是
现在是整数。符号位0指数为123(01111011),有效位精度为10011001100110011001100,整数为
正如你所看到的,最后一位不相等。这是因为我截断了数字。CPU和编译器知道这是有效位之后的东西,精度不能保持,只是把最后一位设置为1。
xsuvu9jc4#
另一个接近精确的问题与这个问题有关,因此多年来的答案。我不认为以上的答案是完整的。
int fun1 ( void ) { float x=0.7; if(x==0.7) return(1); else return(0); } int fun2 ( void ) { float x=1.1; if(x==1.1) return(1); else return(0); } int fun3 ( void ) { float x=1.0; if(x==1.0) return(1); else return(0); } int fun4 ( void ) { float x=0.0; if(x==0.0) return(1); else return(0); } int fun5 ( void ) { float x=0.7; if(x==0.7f) return(1); else return(0); } float fun10 ( void ) { return(0.7); } double fun11 ( void ) { return(0.7); } float fun12 ( void ) { return(1.0); } double fun13 ( void ) { return(1.0); } Disassembly of section .text: 00000000 <fun1>: 0: e3a00000 mov r0, #0 4: e12fff1e bx lr 00000008 <fun2>: 8: e3a00000 mov r0, #0 c: e12fff1e bx lr 00000010 <fun3>: 10: e3a00001 mov r0, #1 14: e12fff1e bx lr 00000018 <fun4>: 18: e3a00001 mov r0, #1 1c: e12fff1e bx lr 00000020 <fun5>: 20: e3a00001 mov r0, #1 24: e12fff1e bx lr 00000028 <fun10>: 28: e59f0000 ldr r0, [pc] ; 30 <fun10+0x8> 2c: e12fff1e bx lr 30: 3f333333 svccc 0x00333333 00000034 <fun11>: 34: e28f1004 add r1, pc, #4 38: e8910003 ldm r1, {r0, r1} 3c: e12fff1e bx lr 40: 66666666 strbtvs r6, [r6], -r6, ror #12 44: 3fe66666 svccc 0x00e66666 00000048 <fun12>: 48: e3a005fe mov r0, #1065353216 ; 0x3f800000 4c: e12fff1e bx lr 00000050 <fun13>: 50: e3a00000 mov r0, #0 54: e59f1000 ldr r1, [pc] ; 5c <fun13+0xc> 58: e12fff1e bx lr 5c: 3ff00000 svccc 0x00f00000 ; IMB
为什么fun 3和fun 4返回一个而不返回其他的?为什么fun 5工作?这是关于语言的。语言说0.7是双精度的,除非你用这个语法0.7f,否则它是单精度的。所以
float x=0.7;
将双精度0.7转换为单精度并存储在x中。
if(x==0.7) return(1);
语言说我们必须提升到更高的精度,这样x中的单精度就转换成了双精度,并与双精度0.7进行比较。
00000028 <fun10>: 28: e59f0000 ldr r0, [pc] ; 30 <fun10+0x8> 2c: e12fff1e bx lr 30: 3f333333 svccc 0x00333333 00000034 <fun11>: 34: e28f1004 add r1, pc, #4 38: e8910003 ldm r1, {r0, r1} 3c: e12fff1e bx lr 40: 66666666 strbtvs r6, [r6], -r6, ror #12 44: 3fe66666 svccc 0x00e66666
单股3f 333333双股3fe 666666666666正如Alexandr所指出的,如果答案仍然是IEEE 754,则单个看啊看啊而双是看啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊小数位数是52位而不是23位。
00111111001100110011... single 001111111110011001100110... double 0 01111110 01100110011... single 0 01111111110 01100110011... double
就像以10为基数的1/3是0.3333333一样......永远。我们这里有一个重复的模式0110
01100110011001100110011 single, 23 bits 01100110011001100110011001100110.... double 52 bits.
这就是答案。
x包含011001100110011001100110011作为它的分数,当它被转换回两倍时,分数是
01100110011001100110011000000000....
不等于
01100110011001100110011001100110...
但在这里
if(x==0.7f) return(1);
相同的位模式相互比较时不会发生这种提升。为什么1.0可以工作?
00000048 <fun12>: 48: e3a005fe mov r0, #1065353216 ; 0x3f800000 4c: e12fff1e bx lr 00000050 <fun13>: 50: e3a00000 mov r0, #0 54: e59f1000 ldr r1, [pc] ; 5c <fun13+0xc> 58: e12fff1e bx lr 5c: 3ff00000 svccc 0x00f00000 ; IMB 0011111110000000... 0011111111110000000... 0 01111111 0000000... 0 01111111111 0000000...
在这两种情况下,分数都是零。因此,从双精度到单精度再到双精度的转换不会损失精度。它可以精确地从单精度转换为双精度,并且两个值的位比较有效。最高的投票和检查的答案由halfdan是正确的答案,这是一个混合精度的情况下,你永远不应该做等于比较。答案中没有显示为什么。0.7不及格1.0有效。为什么0.7不及格没有显示。重复的问题1.1也不及格。
在这里,等号可以从问题中去掉,这是一个已经得到回答的不同问题,但它是同一个问题,也有“什么......”的最初震惊。
int fun1 ( void ) { float x=0.7; if(x<0.7) return(1); else return(0); } int fun2 ( void ) { float x=0.6; if(x<0.6) return(1); else return(0); } Disassembly of section .text: 00000000 <fun1>: 0: e3a00001 mov r0, #1 4: e12fff1e bx lr 00000008 <fun2>: 8: e3a00000 mov r0, #0 c: e12fff1e bx lr
为什么一个显示为小于而另一个显示为不小于?当它们应该相等时。从上面我们知道0.7的故事。
01100110011001100110011 single, 23 bits 01100110011001100110011001100110.... double 52 bits. 01100110011001100110011000000000....
小于。
0.6是不同的重复图案0011而不是0110。但是当从双通道转换为单通道时,或者通常当表示为单通道IEEE 754时。
00110011001100110011001100110011.... double 52 bits. 00110011001100110011001 is NOT the fraction for single 00110011001100110011010 IS the fraction for single
IEEE 754使用舍入模式、上舍入向下舍入或舍入到零。编译器默认情况下倾向于向上舍入。如果你还记得在小学时的舍入12345678,如果我想舍入到从上到下的第三位,它将是12300000,但舍入到下一位1235000,如果后面的数字是5或更大,然后向上舍入。5是1/10中的2,二进制1中的基数(十进制)是基数的1/2,所以如果我们要舍入的位置后面的数字是1,那么就向上舍入,否则就不向上舍入,所以对于0.7我们不向上舍入,对于0.6我们向上舍入。现在很容易看出
00110011001100110011010
由于(x〈0.7)而转换为双精度型
00110011001100110011010000000000....
大于
00110011001100110011001100110011....
型因此,即使不讨论使用等于,问题仍然存在,0.7是双精度的,0.7f是单精度的,如果它们不同,运算将提升到最高精度。
xdyibdwo5#
正如其他评论者所指出的,您面临的问题是,测试浮点数之间的精确等价性通常是不安全的,因为初始化错误或计算中的舍入错误可能会引入微小的差异,导致==运算符返回false。更好的做法是执行以下操作
float f = 0.7; if( fabs(f - 0.7) < FLT_EPSILON ) printf("equal"); else printf("not equal");
假设FLT_EPSILON已定义为适合您的平台的较小浮点值。由于舍入或初始化误差不太可能超过FLT_EPSILON的值,因此这将给予您提供所需的可靠等效性测试。
neekobn86#
网上的很多答案都犯了一个错误,即看浮点数之间的绝对差异,这只对特殊情况有效,可靠的方法是看相对差异,如下所示:
// Floating point comparison: bool CheckFP32Equal(float referenceValue, float value) { const float fp32_epsilon = float(1E-7); float abs_diff = std::abs(referenceValue - value); // Both identical zero is a special case if( referenceValue==0.0f && value == 0.0f) return true; float rel_diff = abs_diff / std::max(std::abs(referenceValue) , std::abs(value) ); if(rel_diff < fp32_epsilon) return true; else return false; }
pw9qyyiw7#
想想这个
int main() { float a = 0.7; if(0.7 > a) printf("Hi\n"); else printf("Hello\n"); return 0; }
if**(0.7〉a)**这里a是一个浮点变量,0.7是一个双精度常量。双精度常量0.7大于浮点变量a。因此if条件满足,输出'Hi'
0.7
'Hi'
范例:
int main() { float a=0.7; printf("%.10f %.10f\n",0.7, a); return 0; }
输出:
0.69999999881亿美元
1rhkuytd8#
变量和常量中保存的指针值的数据类型不同,这是数据类型精度的差异,如果将f变量的数据类型改为double,则打印equal,这是因为默认情况下,double中保存的是浮点型常量,long中保存的是非浮点型常量,double的精度比float高。如果您看到浮点数转换为二进制conversion的方法,就会完全清楚
8条答案
按热度按时间gev0vcfq1#
这是因为在你的陈述中
0.7被视为双精度浮点数。尝试使用0.7f以确保该值被视为浮点数:
但正如Michael在下面的注解中所建议的,永远不要测试浮点值是否完全相等。
webghufk2#
这个答案补充了现有的答案:注意0.7不能精确地表示为浮点数(或双精度数)。如果它被精确地表示,那么在转换为浮点数然后再转换回双精度数时就不会丢失信息,也就不会有这个问题。
甚至可以说,对于不能精确表示的文字浮点常量,应该有一个编译器警告,特别是当标准对于舍入是在运行时以当时设置的模式进行,还是在编译时以另一种舍入模式进行时,是如此模糊时。
所有可以精确表示的非整数都以
5
作为最后一位十进制数字。不幸的是,匡威就不成立了:有些数的最后一个十进制数字是5
,因此无法精确表示。2小整数都可以精确表示,只要不进入反规格化数的领域,除以2的幂就可以将一个可以表示的数转换为另一个可以表示的数。nbysray53#
首先,让我们看看浮点数的内部。我取0.1f,它是4字节长(二进制32),十六进制是
按标准IEEE 754把它转换成十进制我们必须这样做:
在二进制3D CC中,CC CD为
这里第一个数字是符号位。0表示(-1)^0,表示我们的数是正数。
第二个8位是一个指数。二进制为01111011,十进制为123。但实际的指数是123 - 127(总是127)=-4,这意味着我们需要将得到的数字乘以2 ^(-4)。
最后23个字节是有效位精度。第一位乘以1/(2^1)(0.5),第二位乘以1/(2^2)(0.25),依此类推。下面是我们得到的结果:
第一次
我们需要把所有的数字(2的幂)相加,然后再加上1(通常都是1)。
1,60000002384185791015625
现在我们把这个数乘以2 ^(-4),它来自指数。我们只是把上面的数除以2四次:
我使用MS计算器
**
现在是第二部分。从十进制转换为二进制。
第二次
我取数字0.1
因为没有整数部分。第一个符号位-它是0。指数和有效位精度,我现在将计算。逻辑是乘以2整数(0.1 * 2 = 0.2),如果它大于1,减去并继续。
数字是.0001100110011001100110011001100110011,标准表示在得到1之前我们必须左移。你看我们需要4次移位,从这个数字计算指数(127 - 4 =123)。现在的有效位精度是
现在是整数。符号位0指数为123(01111011),有效位精度为10011001100110011001100,整数为
正如你所看到的,最后一位不相等。这是因为我截断了数字。CPU和编译器知道这是有效位之后的东西,精度不能保持,只是把最后一位设置为1。
xsuvu9jc4#
另一个接近精确的问题与这个问题有关,因此多年来的答案。我不认为以上的答案是完整的。
为什么fun 3和fun 4返回一个而不返回其他的?为什么fun 5工作?
这是关于语言的。语言说0.7是双精度的,除非你用这个语法0.7f,否则它是单精度的。所以
将双精度0.7转换为单精度并存储在x中。
语言说我们必须提升到更高的精度,这样x中的单精度就转换成了双精度,并与双精度0.7进行比较。
单股3f 333333双股3fe 666666666666
正如Alexandr所指出的,如果答案仍然是IEEE 754,则单个
看啊看啊
而双是
看啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊
小数位数是52位而不是23位。
就像以10为基数的1/3是0.3333333一样......永远。我们这里有一个重复的模式0110
这就是答案。
x包含011001100110011001100110011作为它的分数,当它被转换回两倍时,分数是
不等于
但在这里
相同的位模式相互比较时不会发生这种提升。
为什么1.0可以工作?
在这两种情况下,分数都是零。因此,从双精度到单精度再到双精度的转换不会损失精度。它可以精确地从单精度转换为双精度,并且两个值的位比较有效。
最高的投票和检查的答案由halfdan是正确的答案,这是一个混合精度的情况下,你永远不应该做等于比较。
答案中没有显示为什么。0.7不及格1.0有效。为什么0.7不及格没有显示。重复的问题1.1也不及格。
编辑
在这里,等号可以从问题中去掉,这是一个已经得到回答的不同问题,但它是同一个问题,也有“什么......”的最初震惊。
为什么一个显示为小于而另一个显示为不小于?当它们应该相等时。
从上面我们知道0.7的故事。
小于。
0.6是不同的重复图案0011而不是0110。
但是当从双通道转换为单通道时,或者通常当表示为单通道IEEE 754时。
IEEE 754使用舍入模式、上舍入向下舍入或舍入到零。编译器默认情况下倾向于向上舍入。如果你还记得在小学时的舍入12345678,如果我想舍入到从上到下的第三位,它将是12300000,但舍入到下一位1235000,如果后面的数字是5或更大,然后向上舍入。5是1/10中的2,二进制1中的基数(十进制)是基数的1/2,所以如果我们要舍入的位置后面的数字是1,那么就向上舍入,否则就不向上舍入,所以对于0.7我们不向上舍入,对于0.6我们向上舍入。
现在很容易看出
由于(x〈0.7)而转换为双精度型
大于
型
因此,即使不讨论使用等于,问题仍然存在,0.7是双精度的,0.7f是单精度的,如果它们不同,运算将提升到最高精度。
xdyibdwo5#
正如其他评论者所指出的,您面临的问题是,测试浮点数之间的精确等价性通常是不安全的,因为初始化错误或计算中的舍入错误可能会引入微小的差异,导致==运算符返回false。
更好的做法是执行以下操作
假设FLT_EPSILON已定义为适合您的平台的较小浮点值。
由于舍入或初始化误差不太可能超过FLT_EPSILON的值,因此这将给予您提供所需的可靠等效性测试。
neekobn86#
网上的很多答案都犯了一个错误,即看浮点数之间的绝对差异,这只对特殊情况有效,可靠的方法是看相对差异,如下所示:
pw9qyyiw7#
想想这个
if**(0.7〉a)**这里a是一个浮点变量,
0.7
是一个双精度常量。双精度常量0.7
大于浮点变量a。因此if条件满足,输出'Hi'
范例:
输出:
0.69999999881亿美元
1rhkuytd8#
变量和常量中保存的指针值的数据类型不同,这是数据类型精度的差异,如果将f变量的数据类型改为double,则打印equal,这是因为默认情况下,double中保存的是浮点型常量,long中保存的是非浮点型常量,double的精度比float高。如果您看到浮点数转换为二进制conversion的方法,就会完全清楚