Repository: numpy/numpy

Issue:
linspace does not properly handle denormal values
This is pretty low priority since AFAIK no user has actually run into this, but, since it [was pointed out on python-ideas](http://news.gmane.org/gmane.comp.python.ideas):

```
In [4]: np.linspace(0, 2e-323, 10)
Out[4]: 
array([  0.00000000e+000,   0.00000000e+000,   0.00000000e+000,
         0.00000000e+000,   0.00000000e+000,   0.00000000e+000,
         0.00000000e+000,   0.00000000e+000,   0.00000000e+000,
         1.97626258e-323])
```

The problem is that `linspace` does `step = (stop - start) / (samples - 1)`, and here that becomes

```
In [5]: (2e-323 - 0) / 9
Out[5]: 0.0
```

The correct output would be something like `[0, 0, 5e-324, 5e-324, ...]`.



Discussion:
The obvious solution to this is to transform the algorithm to do the multiplication before the division. For example:

```
(stop*i + start*(num-i-1)) / (num-1)
```

And the obvious problem is that, having done that, large values now overflow to infinity, instead of small values underflowing to 0, which to me seems just as bad.

The only way I can immediately think of to avoid both problems is to have both algorithms and switch between them based on the size of the inputs… but maybe someone more numerically sophisticated has a better answer. (There were three other alternatives suggested on python-ideas, but they're all effectively equivalent to this one.)

Meanwhile, the rearranged implementation has another advantage: while they both have about the same amount of total error distributed nicely as 1ulp errors around the range, and each one looks nicer than the other in some cases, in the most common cases where there's a difference, it's generally the alternative version that looks nicer. For example, linspace(0, 10, 11)[3] will give you rounded-up 0.30000000000000004 with the current implementation, but rounded-down 0.3 with the alternative. (Of course NumPy's own display shows both of these as 0.3, but when you extract the values as Python floats you can see the difference.)

With the same "divide before multiplying" idea, I don't think that something like :

```
np.arange(num, dtype=dtype) / (num-1) * (stop - start) + start
```

will overflow to infinity any more than what we have right now. Conceptually it is the same as always creating `linspace(0, 1, num)`, which should never be a problem (I think...) and then rescaling and translating.

The only obvious drawback is that we then do the most expensive operation (division), `num` times instead of just one, so there will be a performance hit. I would say that `linspace` is more in need of correctness than it is of performance though.


Resolving patch:
diff --git a/numpy/core/function_base.py b/numpy/core/function_base.py
--- a/numpy/core/function_base.py
+++ b/numpy/core/function_base.py
@@ -11,7 +11,7 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None):
     Return evenly spaced numbers over a specified interval.
 
     Returns `num` evenly spaced samples, calculated over the
-    interval [`start`, `stop` ].
+    interval [`start`, `stop`].
 
     The endpoint of the interval can optionally be excluded.
 
@@ -83,28 +83,45 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None):
     """
     num = int(num)
 
-    # Convert float/complex array scalars to float, gh-3504 
+    # Convert float/complex array scalars to float, gh-3504
     start = start * 1.
     stop = stop * 1.
 
+    dt = result_type(start, stop, float(num))
     if dtype is None:
-        dtype = result_type(start, stop, float(num))
+        dtype = dt
 
     if num <= 0:
         return array([], dtype)
+    if num == 1:
+        return array([start], dtype=dtype)
+    y = _nx.arange(0, num, dtype=dt)
+    if endpoint:
+        num -= 1
+    y /= num
+    y *= stop - start
+    y += start
     if endpoint:
-        if num == 1:
-            return array([start], dtype=dtype)
-        step = (stop-start)/float((num-1))
-        y = _nx.arange(0, num, dtype=dtype) * step + start
         y[-1] = stop
-    else:
-        step = (stop-start)/float(num)
-        y = _nx.arange(0, num, dtype=dtype) * step + start
+
     if retstep:
-        return y.astype(dtype), step
+        return y.astype(dtype, copy=False), (stop - start) / num
     else:
-        return y.astype(dtype)
+        return y.astype(dtype, copy=False)
+
+    # if endpoint:
+        # if num == 1:
+            # return array([start], dtype=dtype)
+        # step = (stop-start)/float((num-1))
+        # y = _nx.arange(0, num, dtype=dtype) * step + start
+        # y[-1] = stop
+    # else:
+        # step = (stop-start)/float(num)
+        # y = _nx.arange(0, num, dtype=dtype) * step + start
+    # if retstep:
+        # return y.astype(dtype), step
+    # else:
+        # return y.astype(dtype)
 
 
 def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None):


Test patch:
diff --git a/numpy/core/tests/test_function_base.py b/numpy/core/tests/test_function_base.py
--- a/numpy/core/tests/test_function_base.py
+++ b/numpy/core/tests/test_function_base.py
@@ -1,7 +1,7 @@
 from __future__ import division, absolute_import, print_function
 
 from numpy.testing import *
-from numpy import logspace, linspace, dtype, array
+from numpy import logspace, linspace, dtype, array, finfo, typecodes
 
 class TestLogspace(TestCase):
 
@@ -76,40 +76,47 @@ def test_complex(self):
         t2 = array([  0.0+1.j  ,   2.5+0.75j,   5.0+0.5j ,   7.5+0.25j,  10.0+0.j])
         assert_equal(lim1, t1)
         assert_equal(lim2, t2)
-        
+
     def test_physical_quantities(self):
         class PhysicalQuantity(float):
             def __new__(cls, value):
                 return float.__new__(cls, value)
-                
+
             def __add__(self, x):
                 assert_(isinstance(x, PhysicalQuantity))
                 return PhysicalQuantity(float(x) + float(self))
             __radd__ = __add__
-                
+
             def __sub__(self, x):
                 assert_(isinstance(x, PhysicalQuantity))
                 return PhysicalQuantity(float(self) - float(x))
-                
+
             def __rsub__(self, x):
                 assert_(isinstance(x, PhysicalQuantity))
                 return PhysicalQuantity(float(x) - float(self))
-                
+
             def __mul__(self, x):
                 return PhysicalQuantity(float(x) * float(self))
             __rmul__ = __mul__
-                
+
             def __div__(self, x):
                 return PhysicalQuantity(float(self) / float(x))
-                
+
             def __rdiv__(self, x):
                 return PhysicalQuantity(float(x) / float(self))
 
-        
+
         a = PhysicalQuantity(0.0)
         b = PhysicalQuantity(1.0)
         assert_equal(linspace(a, b), linspace(0.0, 1.0))
 
+    def test_denormal_numbers(self):
+        # Regression test for gh-5437. Will probably fail when compiled
+        # with ICC, which flushes denormals to zero
+        for dt in (dtype(f) for f in typecodes['Float']):
+            stop = finfo(dt).tiny * finfo(dt).resolution
+            assert_(any(linspace(0, stop, 10, endpoint=False, dtype=dt)))
+
 
 if __name__ == "__main__":
     run_module_suite()