-- Zoom.Quiet [2004-08-11 01:34:18]

# 生成一个浮点数定义的range

.16 Spanning a Range Defined by Floats

Credit: Dinu C. Gherman, Paul M. Winkler

## 问题 Problem

You need an arithmetic progression, just like the built-in function range, but with float values (range works only on integers).

## 解决 Solution

Although this functionality is not available as a built-in, it's not hard to code it with a loop:

```   1 def frange(start, end=None, inc=1.0):
2     "A range-like function that does accept float increments..."
3
4     if end == None:
5         end = start + 0.0     # Ensure a float value for 'end'
6         start = 0.0
7     assert inc                # sanity check
8
9     L = []
10     while 1:
11         next = start + len(L) * inc
12         if inc > 0 and next >= end:
13             break
14         elif inc < 0 and next <= end:
15             break
16         L.append(next)
17
18     return L
```

## 讨论 Discussion

Sadly missing in the Python standard library, the function in this recipe lets you use ranges, just as with the built-in function range, but with float arguments.

Many theoretical restrictions apply, but this function is more useful in practice than in theory. People who work with floating-point numbers all the time have many war stories about billion-dollar projects that failed because someone did not take into consideration the strange things that modern hardware does when comparing floating-point numbers. But for pedestrian cases, simple approaches like this recipe generally work.

You can get a substantial speed boost by preallocating the list instead of calling append repeatedly. This also allows you to get rid of the conditionals in the inner loop. For one element, this version is barely faster, but with more than 10 elements it's consistently about 5 times faster梩he kind of performance ratio that is worth caring about. I get identical output for every test case I can think of:

```   1 def frange2(start, end=None, inc=1.0):
2     "A faster range-like function that does accept float increments..."
3     if end == None:
4         end = start + 0.0
5         start = 0.0
6     else: start += 0.0 # force it to be a float
7
8     count = int((end - start) / inc)
9     if start + count * inc != end:
10         # Need to adjust the count. AFAICT, it always comes up one short.
11         count += 1
12
13     L = [start] * count
14     for i in xrange(1, count):
15         L[i] = start + i * inc
16
17     return L
```

Both versions rely on a single multiplication and one addition to compute each item, to avoid accumulating error by repeated additions. This is why, for example, the body of the for loop in frange2 is not:

`L[i] = L[i-1] + inc `

In Python 2.2, if all you need to do is loop on the result of frange, you can save some memory by turning this function into a simple generator, yielding an iterator when you call it:

```   1 from _ _future_ _ import generators
2
3 def frangei(start, end=None, inc=1.0):
4     "An xrange-like simple generator that does accept float increments..."
5
6     if end == None:
7         end = start + 0.0
8         start = 0.0
9     assert inc                # sanity check
10
11     i = 0
12     while 1:
13         next = start + i * inc
14         if inc > 0 and next >= end:
15             break
16         elif inc < 0 and next <= end:
17             break
18         yield next
19         i += 1
```

If you use this recipe a lot, you should probably take a look at Numeric Python and other third-party packages that take computing with floating-point numbers seriously. This recipe, for example, will not scale well to very large ranges, while those defined in Numeric Python will.

Documentation for the range built-in function in the Library Reference; Numeric Python (http://www.pfdubois.com/numpy/).

• Page.execute = 1.219s
• _moinSearch = 0.009s
• getACL = 0.140s
• getPageCount = 0.150s
• getPageList = 0.007s
• init = 0.030s