为什么 Octave 舍入为 1 'earlier' 而不是 0?

Why does Octave round to 1 'earlier' than 0?

上下文:

在 Octave 中,我为 returns 值介于 0 和 1 之间的 Sigmoid 函数编写了代码;在理想情况下,它只会 return 0 代表 -Inf 和 1 代表 +Inf 但由于浮点不精确,非常接近这两个值的值被四舍五入。

问题:

我的问题是为什么会出现以下情况:0 和 1 的舍入边界明显不同:

>> sigmoid(-709)
ans =   1.2168e-308
>> sigmoid(-710)
ans = 0
>> sigmoid(36)
ans =  1.00000
>> sigmoid(37)
ans =  1
>> (sigmoid(37)-1)==0
ans = 1
>> (sigmoid(36)-1)==0
ans = 0
>> sigmoid(-710)==0
ans = 1
>> sigmoid(-709)==0
ans = 0

在示例中,可以看到将输出四舍五入为 1 所需的值比四舍五入为 0 所需的值小得多。37 与 -710 相比是一个非常大的差异,因为它们应该是大小相同但符号相反...

我的代码:

可能是我的功能有问题:

function [z] = sigmoid(x)
z = 1.0 ./(1.0+exp(-x));
endfunction

我尝试过的:

另一点是,我更改了函数以将结果加 1(本质上是将图形向上平移 1),并且 2 和 1 的边界分别变为 +/-37 - 这让我觉得它确实如此特别是与 0 有关,而不仅仅是函数及其下限。

如果是我电脑的问题,那是什么原因造成的?

首先,复习this brilliant answer by gnovice on floating-point representation

顺便说一句,让我们看看您在这里看到的内容:您可以计算出一个非常接近于零的值:sigmoid(-709) 约等于 1.2e-308,但您不能计算一个类似接近 1 的值:sigmoid(709) 正好等于 1,而不是 1 - 1.2e-308,甚至 sigmoid(36) == 1,而不是略小于 1 的值。

但是当我们知道浮点数在内存中是如何存储的时候,我们就会发现1 - 1.2e-308是无法精确表示的。我们需要 308 位十进制数字来准确表示这个数字。双精度浮点数(Octave 中的默认值)有大约 15 个十进制数字。即1 - 1e-16可以表示,1 - 1e-17不能表示

eps(1)的值为2.2204e-16,这是我们可以用双精度浮点数编码的与1的最小差值。

但是接近 0 的值可以更精确地表示:eps(0)4.9407e-324。这是因为 1.2e-308 这样的值不需要表示 308 个十进制数字,而只需要 2 个,指数中的值为 -308。

无论如何,如果您依赖于远离转换位置的 sigmoid 函数的精确值,那么您的代码逻辑有问题。

如果你想让这个函数对称,你所能做的就是降低低端的精度。有两种方法可以做到这一点:

  1. 只需将非常小的值设置为零,这样 z==0 与另一侧的 z==1 在同一点到达:

    function z = sigmoid(x)
      z = 1.0 ./ (1.0+exp(-x));
      z(z < eps(1)) = 0;
    end
    
  2. 始终计算函数的右半部分,然后返回负输入。这使得x=0两边的计算误差对称:

    function z = sigmoid(x)
      z = 1.0 ./ (1.0+exp(-abs(x)));
      I = x < 0;
      z(I) = 0.5 - z(I);
    end