浮点值 0.0 的表示方式是否与其他浮点值不同?

Is a floating-point value of 0.0 represented differently from other floating-point values?

我一直在翻阅我的 C++ 书籍,我遇到了一个声明,它说零可以精确地表示为浮点数。我想知道这怎么可能,除非 0.0 的值存储为浮点值以外的类型。我写了下面的代码来测试这个:

#include <iomanip>
#include <iostream>

int main()
{
    float value1 {0.0};
    float value2 {0.1};

    std::cout << std::setprecision(10) << std::fixed;

    std::cout << value1 << '\n' 
              << value2 << std::endl;
}

运行 此代码给出了以下输出:

0.0000000000
0.1000000015

到10位精度,0.0还是0,0.1有一些不准确(这是意料之中的)。值 0.0 在表示方式上是否与其他浮点数不同,这是编译器或计算机体系结构的特性吗?

2如何表示为一个精确的数字? 4? 15? 0.5?答案只是有些数字可以精确地用浮点格式表示(基于base-2/binary),而有些则不能。

这与十进制没有区别。你不能用十进制表示 1/3,但这并不意味着你不能表示 0.

零在某种程度上是特殊的,因为(像其他实数一样)证明这个 属性 比一些任意小数更简单。但仅此而已。

所以:

what is it about these values (0, 1/16, 1/2048, ...) that allows them to be represented exactly.

简单的数学。在任何给定的基数中,在我们正在讨论的表示形式中,一些数字可以用固定的小数位数写出;其他人不能。就是这样。

您可以在线玩 H. Schmidt's IEEE-754 Floating Point Converter 不同的数字,以查看一堆不同的表示形式,以及编码到这些表示形式中会出现哪些错误。对于初学者,请尝试 0.5、0.2 和 0.1。

It was my (perhaps naive) understanding that all floating point values contained some instability.

不,绝对不是。

您希望将程序中的每个浮点值视为可能存在一些小错误,因为您通常不知道 是什么计算顺序导致的。你不能相信它,一般来说。我希望过去有人把这个教给你一半,这就是导致你误解的原因。

但是,如果您确实知道在创建值(例如"all I've done is initialised it to zero")的每个步骤中涉及的错误(或缺少错误),那很好!那就不用担心了。

这是一种看待这种情况的方法:用 64 位来存储一个数字,有 2^64 位模式。其中一些是 "not-a-number" 表示,但大多数 2^64 模式表示数字。表示的数字是准确表示的,没有错误。在了解 floating point math 之后,这可能看起来很奇怪;一个警告潜伏在前方。

但是,2^64那么大,实数还有无穷多。当计算产生非整数结果时,答案很可能不是由 2^64 模式之一表示的数字。也有例外。例如,1/2 由其中一种模式表示。如果将 0.5 存储在浮点变量中,它实际上将存储 0.5。让我们试试其他个位数的分母。 (注:我写分数是为了表现力,不是整数运算。)

  • 1/1 – 正好存储
  • 1/2 – 正好存储
  • 1/3 – 未准确存储
  • 1/4 – 正好存储
  • 1/5 – 未准确存储
  • 1/6 – 未准确存储
  • 1/7 – 未准确存储
  • 1/8 – 正好存储
  • 1/9 – 未准确存储

因此,对于这些简单的示例,超过一半的示例并未准确存储。当您进行更复杂的计算时,任何一项计算都会让您脱离精确表示的孤岛。您明白为什么一般的经验法则是浮点值不精确吗?很容易落入那个境界。避免它是可能的,但不要指望它。

有些数字可以用浮点值精确表示。大多数不能。