11

诸如“为什么不是 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1 = 0.8? ”之类的问题让我想到......

...让编译器警告浮点常量可能会很好,它会四舍五入到二进制浮点类型中最接近的可表示值(例如,0.1 和 0.8 在 radix-2 浮点中四舍五入,否则它们需要无限的空间来存储无限的数字)。

我已经查找了 gcc 警告,但到目前为止没有发现任何用于此目的的警告(-Wall, -Wextra, -Wfloat-equal, -Wconversion, -Wcoercion(不支持或仅限 C?),-Wtraditional(仅限 C)似乎没有做我想做的事)。

我也没有在 Microsoft Visual C++ 编译器中发现这样的警告。

我错过了隐藏或很少使用的选项吗?

有没有任何编译器有这种警告?

编辑:此警告可能对教育目的有用,并提醒那些刚接触浮点的人。

4

4 回答 4

8

编译器无法发出此类警告没有技术原因。然而,它们只对学生有用(他们应该在开始认真工作之前了解浮点算术是如何工作的)和对浮点做非常好的工作的人。不幸的是,大多数浮点工作都很粗糙。人们向计算机扔数字,而不考虑计算机的工作方式,他们接受他们得到的任何结果。

默认情况下必须关闭警告以支持大量现有浮点代码。如果它可用,我会在 Mac OS X 数学库中为我的代码打开它。当然,库中的某些点我们依赖于浮点值的每一位,例如我们使用扩展精度算术的地方,并且值通过多个浮点对象表示(例如,我们将有一个1/π 的高位对象,另一个对象的 1/π 减去第一个对象,第三个对象的 1/π 减去前两个对象,给我们大约 150 位的 1/π)。一些这样的值在源文本中以十六进制浮点数表示,以避免编译器转换十进制数字时出现任何问题,并且我们可以轻松地转换任何剩余的数字以避免新的编译器警告。

但是,我怀疑我们能否说服编译器开发人员相信足够多的人会使用此警告,或者它会捕获足够多的错误以使其值得他们花时间。考虑 libm 的情况。假设我们通常为所有常数写精确的数字,但有一次,写了一些其他数字。这个警告会捕获一个错误吗?那么,有什么错误?最有可能的是,无论如何,数字都会被转换为我们想要的值。在打开此警告的情况下编写代码时,我们可能会考虑如何执行浮点计算,并且我们编写的值是适合我们目的的值。例如,它可能是我们计算的某个极小极大多项式的系数,并且该系数与将要得到的一样好,

所以,这个警告很少会发现错误。也许它会捕捉到我们打错数字的情况,不小心在十六进制浮点数字中插入了一个额外的数字,导致它超出了可表示的有效位。但这很少见。在大多数情况下,我们使用的数字要么简单而简短,要么是从计算它们的软件中复制和粘贴的。在某些情况下,我们会手动键入特殊值,例如 0x1.fffffffffffffp0。当额外的“f”滑入该数字时发出警告可能会在编译期间发现错误,但几乎可以肯定在测试中会很快发现该错误,因为它会极大地改变特殊值。

因此,这样的编译器警告几乎没有什么用处:很少有人会使用它,而且它会为使用它的人捕获很少的错误。

于 2012-09-27T13:37:22.623 回答
1

警告在源代码中:当您编写float,doublelong double包含它们各自的任何文字时。显然,一些文字是精确的,但即使这样也无济于事:两个精确值的总和可能不精确,例如,如果它们具有相当不同的比例。让编译器警告不精确的浮点常量会产生错误的安全感。另外,你打算对舍入常量做什么?明确地写出最接近的值会容易出错并混淆意图。以不同的方式编写它们,例如,编写1.0 / 10.0而不是0.1也会混淆意图并可能产生不同的值。

于 2012-09-27T12:49:18.140 回答
1

不会有这样的编译器开关,原因很明显。我们用十进制写下二进制组件:

第一个小数位是 0.5

第二个小数位是 0.25

第三个小数位是 0.125

……

你看到了吗?由于数字 5 的奇数结尾,每个位都需要另一个小数来准确表示它。一位需要一位小数,两位需要两位小数,依此类推。

因此,对于小数浮点数,这意味着对于大多数十进制数,单精度浮点数需要 24(!)十进制数字,双精度浮点数需要 53(!)十进制数字。更糟糕的是,确切的数字没有额外的信息,它们是由碱基变化引起的纯人工制品。

没有人会为 pi 写下 3.141592653589793115997963468544185161590576171875 以避免编译器警告。

于 2012-09-27T13:23:41.247 回答
-1

我不知道编译器是如何知道的,也不知道编译器会警告您类似的事情。一个数字可以用本质上不准确的东西来精确表示,这只是一个巧合。

于 2012-09-27T10:40:47.423 回答