【问题标题】:problems in floating point comparison [duplicate]浮点比较中的问题[重复]
【发布时间】:2010-10-18 19:40:52
【问题描述】:
void main()
{
    float f = 0.98;
    if(f <= 0.98)
        printf("hi");
    else
        printf("hello");
    getch();
}

我在这里遇到了这个问题。在使用 f 的不同浮点值时,我得到了不同的结果。 为什么会这样?

【问题讨论】:

  • 一般规则:永远不要以“精确”的方式比较浮点数。这没有任何意义。总是使用一些“epsilon”
  • @valdo:如果没有更彻底的分析,这通常是个糟糕的建议。
  • @KeithThompson:不幸的是,你需要看到What should main() return in C and C++?
  • @KeithThompson:当我发现 2008 年以后的 MSVS 文档记录了它时,我和你听起来一样懊恼——这只是一两周前的事。我同意int main(void) 更可取,但我很想知道为什么int main() 不是作为函数定义的扩展版本(配置文件中的电子邮件)。我使用的 GCC 编译器选项意味着我使用 int main(void)int main(int argc, char **argv) 而不是 int main(),但这不是语言律师的论点。
  • @JonathanLeffler:简而言之,int main() 不等于 int main(void)。后者使main(42) 违反约束;前者没有。另一方面,int main() 在 pre-ANSI C 中肯定是正确的,并且 intent 是为了避免破坏旧代码,所以我认为int main() 应该 被允许(但已过时),但恕我直言,标准的当前措辞并未如此说明。

标签: c floating-point floating-point-conversion


【解决方案1】:

f 使用的是float 精度,但默认情况下double 精度为0.98,因此语句f &lt;= 0.98 使用double 精度进行比较。

f 因此在比较中转换为double,但可能会使结果略大于 0.98。

使用

if(f <= 0.98f)

或者使用double 代替f


详细...假设floatIEEE single-precisiondoubleIEEE double-precision

这些类型的浮点数以 base-2 表示形式存储。在 base-2 中,这个数字需要无限精度来表示,因为它是一个重复的小数:

0.98 = 0.1111101011100001010001111010111000010100011110101110000101000...

一个float只能存储24位有效数字,即

       0.111110101110000101000111_101...
                                 ^ round off here
   =   0.111110101110000101001000

   =   16441672 / 2^24

   =   0.98000001907...

double 可以存储 53 位有效数字,所以

       0.11111010111000010100011110101110000101000111101011100_00101000...
                                                              ^ round off here
   =   0.11111010111000010100011110101110000101000111101011100

   =   8827055269646172 / 2^53

   =   0.97999999999999998224...

因此,float 中的 0.98 会稍微变大,double 中会变小。

【讨论】:

  • +1 好点,可能比我的答案更接近他的要求。
【解决方案2】:

这是因为浮点值不是数字的精确表示。所有以十为底的数字都需要在计算机上表示为以 2 为底的数字。正是在这种转换中,精度丢失了。

http://en.wikipedia.org/wiki/Floating_point了解更多信息


一个例子(在我的 VB6 天里遇到这个问题)

要将数字 1.1 转换为单精度浮点数,我们需要将其转换为二进制。有 32 位需要创建。

位 1 是符号位(是负数 [1] 还是位置 [0]) 位 2-9 用于指数值 第 10-32 位用于尾数(也称为有效数,基本上是科学记数法的系数)

所以对于 1.1,单个浮点值存储如下(这是截断值,编译器可能会在后台舍入最低有效位,但我所做的只是截断它,这稍微不太准确,但不会改变这个例子的结果):

s --exp--- -------mantissa--------
0 01111111 00011001100110011001100

如果您注意到尾数中有重复模式 0011。二进制的 1/10 就像十进制的 1/3。它永远持续下去。因此,要从 32 位单精度浮点值中检索值,我们必须首先将指数和尾数转换为十进制数,以便我们可以使用它们。

符号 = 0 = 一个正数

指数:01111111 = 127

尾数:00011001100110011001100 = 838860

我们需要将尾数转换为十进制值。原因是二进制数前面有一个隐含的整数(即 1.00011001100110011001100)。隐含的数字是因为尾数表示要在科学计数法中使用的标准化值:1.0001100110011.... * 2^(x-127)。

为了得到 838860 的十进制值,我们只需除以 2^-23,因为尾数中有 23 位。这给了我们 0.099999904632568359375。将隐含的 1 添加到尾数中得到 1.099999904632568359375。指数是 127,但公式需要 2^(x-127)。

所以这是数学:

(1 + 099999904632568359375) * 2^(127-127)

1.099999904632568359375 * 1 = 1.099999904632568359375

如您所见,1.1 并没有真正存储为 1.1 的单浮点值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-24
    • 2011-02-03
    • 2015-09-06
    • 2023-04-05
    • 2015-03-19
    • 2011-08-20
    相关资源
    最近更新 更多