Bagaimana cara menggunakan nilai langkah rentang desimal ()?

744

Apakah ada cara untuk melangkah antara 0 dan 1 dengan 0,1?

Saya pikir saya bisa melakukannya seperti berikut, tetapi gagal:

for i in range(0, 1, 0.1):
    print i

Sebaliknya, ia mengatakan bahwa argumen langkah tidak boleh nol, yang tidak saya harapkan.

Evan Fosmark
sumber
17
int (0,1) == 0, jadi langkah sebenarnya adalah nol. Mungkin tidak terduga, tetapi nol. Anda mungkin ingin menyatakan kembali pertanyaan Anda untuk mencerminkan fakta bahwa Anda tidak mengharapkannya. Mengatakan "tidak" itu salah dan menyesatkan.
S.Lott
3
BTW Satu liner pendek dapat digulung menggunakan itertools.takewhiledan itertools.count. Itu tidak lebih baik dari drangesegi kinerja.
Kos
1
Sangat memalukan bahwa jangkauan python tidak memungkinkan hal ini, mengingat betapa mudahnya untuk mengimplementasikan generator yang melakukan ini bahkan tanpa mengumpulkan kesalahan pembulatan. Heck, bahkan seqalat dalam GNU coreutils memungkinkan seseorang untuk melakukannya seq 0 0.1 1tanpa kesalahan pembulatan!
josch
3
@josch: seqmenggunakan C long doublejenis internal, dan adalah tunduk pada kesalahan pembulatan. Misalnya pada mesin saya, seq 0 0.1 1berikan 1sebagai output terakhir (seperti yang diharapkan), tetapi seq 1 0.1 2berikan 1.9sebagai output terakhir (bukan yang diharapkan 2).
Mark Dickinson
Untuk kenyamanan, saran @ Kos dapat diimplementasikan sebagai itertools.takewhile(lambda x: (x+0.05)<1, itertools.count(0,0.1))atau itertools.islice(itertools.count(0,0.1), 10)(setelah Anda miliki import itertools), meskipun saya belum menguji mana yang lebih efisien
Anonim

Jawaban:

906

Daripada menggunakan langkah desimal secara langsung, jauh lebih aman untuk mengungkapkan ini dalam hal berapa banyak poin yang Anda inginkan. Jika tidak, kesalahan pembulatan floating-point cenderung memberi Anda hasil yang salah.

Anda dapat menggunakan fungsi linspace dari pustaka NumPy (yang bukan bagian dari pustaka standar tetapi relatif mudah diperoleh). linspacemengambil sejumlah poin untuk kembali, dan juga memungkinkan Anda menentukan apakah akan menyertakan titik akhir yang tepat atau tidak:

>>> np.linspace(0,1,11)
array([ 0. ,  0.1,  0.2,  0.3,  0.4,  0.5,  0.6,  0.7,  0.8,  0.9,  1. ])
>>> np.linspace(0,1,10,endpoint=False)
array([ 0. ,  0.1,  0.2,  0.3,  0.4,  0.5,  0.6,  0.7,  0.8,  0.9])

Jika Anda benar-benar ingin menggunakan nilai langkah floating-point, Anda bisa, dengan numpy.arange.

>>> import numpy as np
>>> np.arange(0.0, 1.0, 0.1)
array([ 0. ,  0.1,  0.2,  0.3,  0.4,  0.5,  0.6,  0.7,  0.8,  0.9])

Kesalahan pembulatan titik-mengambang akan menyebabkan masalah. Inilah kasus sederhana di mana kesalahan pembulatan menyebabkan arangemenghasilkan array panjang-4 ketika seharusnya hanya menghasilkan 3 angka:

>>> numpy.arange(1, 1.3, 0.1)
array([1. , 1.1, 1.2, 1.3])
Andrew Jaffe
sumber
51
numpy adalah komponen python yang ada di mana-mana sehingga saya menganggap jawaban ini sebagai yang paling 'pythonic' dari semua.
serangan udara
21
@AndreTerra Masalahnya adalah bahwa @ numpy @ adalah paket pihak ketiga dan menambahkan banyak overhead dalam hal manajemen ketergantungan, penyimpanan (untuk paket itu sendiri) dll. Tergantung pada apa yang dilakukan pengembang, mungkin mustahil untuk menggunakan Itu.
rbaleksandar
Maafkan saya, tapi saya tidak mengerti kesalahan pembulatan titik apung di bagian terakhir sejak itu np.linspace(1.,1.3,4)dan np.arange(1.,1.3,0.1)memberikan hasil yang persis sama
deadcode
4
@deadcode Alasannya adalah bahwa np.arange didefinisikan untuk menghasilkan rentang [start,stop)(yaitu tidak termasuk stop), jadi orang tidak akan mengharapkan 1.3 untuk dimasukkan dalam daftar. Lihat pertanyaan ini untuk alasan mengapa hal itu masih termasuk dan apa yang harus dilakukan terhadapnya.
dennis
5
Seberapa banyak paket yang digunakan bisa dibilang bukan indikator apakah itu "Pythonic."
Alex Hall
213

Rentang Python () hanya dapat melakukan bilangan bulat, bukan titik mengambang. Dalam kasus spesifik Anda, Anda dapat menggunakan pemahaman daftar sebagai gantinya:

[x * 0.1 for x in range(0, 10)]

(Ganti panggilan untuk menjangkau dengan ekspresi itu.)

Untuk kasus yang lebih umum, Anda mungkin ingin menulis fungsi atau generator khusus.


sumber
36
Bahkan lebih baik, Anda bisa menggunakan pemahaman generator jika Anda bekerja dengan Python 2.4+. (x * 0.1 for x in range(0, 10)).
JAB
42
Bahkan lebih baik, menempatkan x/10bukannya x * 0.1: D Tidak ada yang istimewa sebenarnya, tetapi beberapa angka dalam akan ada lebih tepat, misalnya untuk 3*0.1Anda mendapatkan 0.30000000000000004, sedangkan untuk 3/10 Anda mendapatkan 0.3:)
cowok
4
3/10 memberi saya 0, bukan 0,3. 3 / 10.0 memberi 0,29999999999999999. Python 2.6.
19
@ LarsWirzenius: di Python 2.2+, from __future__ import division; 3/10mengembalikan 0,3. Perilaku ini adalah default dalam Python 3.x.
Benjamin Hodgson
2
fungsi bulat juga dapat digunakan lst = [bulat (x * 0,10,2) untuk x dalam kisaran (0,10)]
MARK
148

Membangun di 'xrange ([mulai], berhenti [, langkah])' , Anda dapat menentukan generator yang menerima dan menghasilkan jenis apa pun yang Anda pilih (tetap pada jenis yang mendukung +dan <):

>>> def drange(start, stop, step):
...     r = start
...     while r < stop:
...         yield r
...         r += step
...         
>>> i0=drange(0.0, 1.0, 0.1)
>>> ["%g" % x for x in i0]
['0', '0.1', '0.2', '0.3', '0.4', '0.5', '0.6', '0.7', '0.8', '0.9', '1']
>>> 
Gimel
sumber
45
Ini memiliki masalah pembulatan. Silakan lihat di sini: code.activestate.com/recipes/66472
Christian Oudard
Saya akan memperluas sedikit untuk arah lain dengan (sementara r> stop) dan langkah r - = yang sesuai untuk memberikan arah yang berlawanan.
user318904
1
Saya melakukan fungsi xfrange tanpa masalah presisi mengambang yang disebutkan di atas. Lihat;) stackoverflow.com/questions/477486/…
Carlos Vega
1
Anda mengumpulkan kesalahan pembulatan. Silakan gunakan ini sebagai gantinya: `i = 0; r = mulai saat r <berhenti: i + = 1; r = mulai + i * langkah; yield r`
Cees Timmerman
1
Ini dari pythoncentral.io/pythons-range-function-explained (dan sumber dokumentasi Python lainnya)
Apostolos
31

Tingkatkan besarnya iuntuk loop dan kemudian kurangi saat Anda membutuhkannya.

for i * 100 in range(0, 100, 10):
    print i / 100.0

EDIT: Jujur saya tidak ingat mengapa saya pikir itu akan bekerja secara sintaksis

for i in range(0, 11, 1):
    print i / 10.0

Itu harus memiliki output yang diinginkan.

cmsjr
sumber
Saya pikir Anda akan menemukan bahwa rentang () bekerja dari bilangan bulat, dalam hal ini ini akan menjadi satu-satunya solusi, menggunakan fungsi yang sama setidaknya.
Matthew Scharley
1
@cmsjr creative: D Hanya satu hal kecil: bagi 100,0 agar Python tidak memotong hasilnya jika Anda menggunakan Python 2.x. Saya pikir di 3.0, itu akan berfungsi karena Anda sudah menyandikannya.
Dana
2
for i * 100 in range(0, 100, 10): SyntaxError: tidak dapat menetapkan ke operator
Anne van Rossum
25

scipymemiliki fungsi arangebawaan yang menyamaratakan range()konstruktor Python untuk memenuhi kebutuhan penanganan float Anda.

from scipy import arange

Catherine Ray
sumber
8
Ini sebenarnya sama persis dengan yang arangeAnda temukan di numpy: >>> import scipy >>> import numpy >>> numpy.arange is scipy.arangeakan kembali True.
iFreilicht
from nump import arange as range
shrewmouse
24

NumPy agak berlebihan, saya pikir.

[p/10 for p in range(0, 10)]
[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]

Secara umum, untuk melakukan langkah-demi- 1/xsampai yAnda akan melakukan

x=100
y=2
[p/x for p in range(0, int(x*y))]
[0.0, 0.01, 0.02, 0.03, ..., 1.97, 1.98, 1.99]

( 1/xMenghasilkan kebisingan pembulatan kurang ketika saya diuji).

Kalle
sumber
18

Mirip dengan fungsi R seq , yang ini mengembalikan urutan dalam urutan apa pun yang diberikan nilai langkah yang benar. Nilai terakhir sama dengan nilai stop.

def seq(start, stop, step=1):
    n = int(round((stop - start)/float(step)))
    if n > 1:
        return([start + step*i for i in range(n+1)])
    elif n == 1:
        return([start])
    else:
        return([])

Hasil

seq(1, 5, 0.5)

[1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0]

seq(10, 0, -1)

[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

seq(10, 0, -2)

[10, 8, 6, 4, 2, 0]

seq(1, 1)

[1]

zeferino
sumber
2
Ini adalah jawaban yang bagus untuk seseorang yang ingin mendapatkannya tanpa terlalu banyak menggunakan python.
Chani
1
Itu hampir apa yang saya cari - perhatikan bahwa seq(0.5, 3.0)kembali [0.5, 1.5, 2.5, 3.5]. Untuk menghindari entri terakhir berada di luar jangkauan, gantikan n = int(round(...dengan n = int(floor(...baris from math import floordi atas (di atas def seq(...).
FriendFX
2
@ FriendsFX Jangan lakukan ini! Jika floordigunakan, seq(0.2, 0.9, 0.1)akan gagal mencapai titik akhir kanan dan akan kembali[0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7, 0.8]
fdermishin
@ user502144: Tangkapan bagus, terima kasih. Saya kira saya harus puas dengan salah satu solusi yang lebih kompleks agar tetap umum.
FriendFX
14

Rentang () fungsi bawaan mengembalikan urutan nilai integer, saya khawatir, jadi Anda tidak dapat menggunakannya untuk melakukan langkah desimal.

Saya akan mengatakan cukup gunakan loop sementara:

i = 0.0
while i <= 1.0:
    print i
    i += 0.1

Jika Anda penasaran, Python mengonversi 0,1 menjadi 0, itulah sebabnya ia memberi tahu Anda bahwa argumennya tidak boleh nol.

Dana
sumber
2
Jangan lakukan ini! Menambahkan .110 kali tidak sama dengan menambahkan 1! docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
cat
12

Inilah solusi menggunakan itertools :

import itertools

def seq(start, end, step):
    if step == 0:
        raise ValueError("step must not be 0")
    sample_count = int(abs(end - start) / step)
    return itertools.islice(itertools.count(start, step), sample_count)

Contoh Penggunaan:

for i in seq(0, 1, 0.1):
    print(i)
Pramod
sumber
Demi kelengkapan, Anda harus menghitung nilai absolut untuk variabel sample_count, dengan cara itu fungsi Anda juga akan berfungsi untuk awal negatif (yaitu dari -10 hingga 10)
Deleteman
10
[x * 0.1 for x in range(0, 10)] 

di Python 2.7x memberi Anda hasil:

[0,0, 0,1, 0,2, 0,30000000000000004, 0,4, 0,5, 0,6000000000000001, 0,700000000000000001, 0,8, 0,9]

tetapi jika Anda menggunakan:

[ round(x * 0.1, 1) for x in range(0, 10)]

memberi Anda yang diinginkan:

[0,0, 0,1, 0,2, 0,3, 0,4, 0,5, 0,6, 0,7, 0,8, 0,9]

Nik
sumber
5

Dan jika Anda sering melakukan ini, Anda mungkin ingin menyimpan daftar yang dihasilkan r

r=map(lambda x: x/10.0,range(0,10))
for i in r:
    print i
RSabet
sumber
5

more_itertoolsadalah perpustakaan pihak ketiga yang mengimplementasikan numeric_rangealat:

import more_itertools as mit


for x in mit.numeric_range(0, 1, 0.1):
    print("{:.1f}".format(x))

Keluaran

0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9

Alat ini juga berfungsi untuk Decimaldan Fraction.

pylang
sumber
4

Versi saya menggunakan fungsi rentang asli untuk membuat indeks multiplikasi untuk perubahan tersebut. Ini memungkinkan sintaks yang sama ke fungsi rentang asli. Saya telah membuat dua versi, satu menggunakan float, dan satu menggunakan Decimal, karena saya menemukan bahwa dalam beberapa kasus saya ingin menghindari penyimpangan pembulatan yang diperkenalkan oleh aritmatika floating point.

Ini konsisten dengan hasil set kosong seperti dalam rentang / xrange.

Melewati hanya satu nilai numerik ke salah satu fungsi akan mengembalikan output rentang standar ke nilai langit-langit integer dari parameter input (jadi jika Anda memberikannya 5,5, itu akan mengembalikan kisaran (6).)

Sunting: kode di bawah ini sekarang tersedia sebagai paket di pypi: Franges

## frange.py
from math import ceil
# find best range function available to version (2.7.x / 3.x.x)
try:
    _xrange = xrange
except NameError:
    _xrange = range

def frange(start, stop = None, step = 1):
    """frange generates a set of floating point values over the 
    range [start, stop) with step size step

    frange([start,] stop [, step ])"""

    if stop is None:
        for x in _xrange(int(ceil(start))):
            yield x
    else:
        # create a generator expression for the index values
        indices = (i for i in _xrange(0, int((stop-start)/step)))  
        # yield results
        for i in indices:
            yield start + step*i

## drange.py
import decimal
from math import ceil
# find best range function available to version (2.7.x / 3.x.x)
try:
    _xrange = xrange
except NameError:
    _xrange = range

def drange(start, stop = None, step = 1, precision = None):
    """drange generates a set of Decimal values over the
    range [start, stop) with step size step

    drange([start,] stop, [step [,precision]])"""

    if stop is None:
        for x in _xrange(int(ceil(start))):
            yield x
    else:
        # find precision
        if precision is not None:
            decimal.getcontext().prec = precision
        # convert values to decimals
        start = decimal.Decimal(start)
        stop = decimal.Decimal(stop)
        step = decimal.Decimal(step)
        # create a generator expression for the index values
        indices = (
            i for i in _xrange(
                0, 
                ((stop-start)/step).to_integral_value()
            )
        )  
        # yield results
        for i in indices:
            yield float(start + step*i)

## testranges.py
import frange
import drange
list(frange.frange(0, 2, 0.5)) # [0.0, 0.5, 1.0, 1.5]
list(drange.drange(0, 2, 0.5, precision = 6)) # [0.0, 0.5, 1.0, 1.5]
list(frange.frange(3)) # [0, 1, 2]
list(frange.frange(3.5)) # [0, 1, 2, 3]
list(frange.frange(0,10, -1)) # []
Nisan.H
sumber
Bagaimana bisa frangebekerja jika berhenti None? Bagian kode itu bahkan tidak mempertimbangkan ukuran langkah lagi.
josch
1
@josch rangememiliki dua tanda tangan :, range(stop)yang mengasumsikan default start=0, step=1, dan range(start, stop, step), di mana tidak ada asumsi yang dibuat. frangemencerminkan itu. Bila menggunakan range(stop)tanda tangan, baik frangedan drangemulai 0 dan kenaikan sebesar 1, sehingga perilaku mereka identik dengan teratur range(stop)perilaku dengan berhenti dibulatkan ke bilangan bulat terdekat.
Nisan.H
4

Ini adalah solusi saya untuk mendapatkan rentang dengan langkah float.
Menggunakan fungsi ini tidak perlu mengimpor numpy, atau menginstalnya.
Saya cukup yakin itu bisa ditingkatkan dan dioptimalkan. Merasa bebas untuk melakukannya dan mempostingnya di sini.

from __future__ import division
from math import log

def xfrange(start, stop, step):

    old_start = start #backup this value

    digits = int(round(log(10000, 10)))+1 #get number of digits
    magnitude = 10**digits
    stop = int(magnitude * stop) #convert from 
    step = int(magnitude * step) #0.1 to 10 (e.g.)

    if start == 0:
        start = 10**(digits-1)
    else:
        start = 10**(digits)*start

    data = []   #create array

    #calc number of iterations
    end_loop = int((stop-start)//step)
    if old_start == 0:
        end_loop += 1

    acc = start

    for i in xrange(0, end_loop):
        data.append(acc/magnitude)
        acc += step

    return data

print xfrange(1, 2.1, 0.1)
print xfrange(0, 1.1, 0.1)
print xfrange(-1, 0.1, 0.1)

Outputnya adalah:

[1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0]
[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1]
[-1.0, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0.0]
Carlos Vega
sumber
1
Ada kesalahan dengan kehilangan nilai terakhir jika berada dalam 1 langkah dari nilai berhenti. yaitu xfrange (1,10,2) hanya 1,3,5,7, hilang 9
Lobe
Untuk referensi dan pembaca lain, harap bandingkan implementasi ini dengan stackoverflow.com/a/477610/54964 ini . Ini tampaknya tidak memiliki masalah pelampung besar.
Léo Léopold Hertz 준영
@carlosvega Bisakah Anda mengonfirmasi mengapa Lobe mendapatkan hasilnya?
Léo Léopold Hertz 준영
3

Untuk kelengkapan butik, solusi fungsional:

def frange(a,b,s):
  return [] if s > 0 and a > b or s < 0 and a < b or s==0 else [a]+frange(a+s,b,s)
Bijou Trouvaille
sumber
2

Anda dapat menggunakan fungsi ini:

def frange(start,end,step):
    return map(lambda x: x*step, range(int(start*1./step),int(end*1./step)))
pengguna376536
sumber
Tampaknya tidak berfungsi dengan benar, misalnyalist(frange(99.8, 100.1, 0.1)) => [99.7, 99.80000000000001, 99.9]
Shai Coleman
2

Trik untuk menghindari putaran-off masalah adalah dengan menggunakan nomor yang terpisah untuk bergerak melalui jangkauan, yang dimulai dan setengah yang langkah maju dari awal .

# floating point range
def frange(a, b, stp=1.0):
  i = a+stp/2.0
  while i<b:
    yield a
    a += stp
    i += stp

Atau, numpy.arangebisa digunakan.

wolfram77
sumber
2

Itu bisa dilakukan menggunakan Numpy library. Fungsi arange () memungkinkan langkah-langkah dalam float. Tapi, ia mengembalikan array numpy yang dapat dikonversi ke daftar menggunakan tolist () untuk kenyamanan kita.

for i in np.arange(0, 1, 0.1).tolist():
   print i
pengguna3654478
sumber
2

Jawaban saya mirip dengan yang lain menggunakan peta (), tanpa perlu NumPy, dan tanpa menggunakan lambda (meskipun Anda bisa). Untuk mendapatkan daftar nilai float dari 0,0 hingga t_max dalam langkah-langkah dt:

def xdt(n):
    return dt*float(n)
tlist  = map(xdt, range(int(t_max/dt)+1))
Eric Myers
sumber
2

Terkejut belum ada yang menyebutkan solusi yang disarankan dalam Python 3 docs :

Lihat juga:

  • The linspace resep menunjukkan bagaimana menerapkan versi malas kisaran yang cocok untuk floating aplikasi point.

Setelah ditentukan, resep mudah digunakan dan tidak memerlukan numpyatau perpustakaan eksternal lainnya, tetapi fungsinya seperti numpy.linspace(). Perhatikan bahwa alih-alih stepargumen, numargumen ketiga menentukan jumlah nilai yang diinginkan, misalnya:

print(linspace(0, 10, 5))
# linspace(0, 10, 5)
print(list(linspace(0, 10, 5)))
# [0.0, 2.5, 5.0, 7.5, 10]

Saya mengutip versi modifikasi dari resep Python 3 lengkap dari Andrew Barnert di bawah ini:

import collections.abc
import numbers

class linspace(collections.abc.Sequence):
    """linspace(start, stop, num) -> linspace object

    Return a virtual sequence of num numbers from start to stop (inclusive).

    If you need a half-open range, use linspace(start, stop, num+1)[:-1].
    """
    def __init__(self, start, stop, num):
        if not isinstance(num, numbers.Integral) or num <= 1:
            raise ValueError('num must be an integer > 1')
        self.start, self.stop, self.num = start, stop, num
        self.step = (stop-start)/(num-1)
    def __len__(self):
        return self.num
    def __getitem__(self, i):
        if isinstance(i, slice):
            return [self[x] for x in range(*i.indices(len(self)))]
        if i < 0:
            i = self.num + i
        if i >= self.num:
            raise IndexError('linspace object index out of range')
        if i == self.num-1:
            return self.stop
        return self.start + i*self.step
    def __repr__(self):
        return '{}({}, {}, {})'.format(type(self).__name__,
                                       self.start, self.stop, self.num)
    def __eq__(self, other):
        if not isinstance(other, linspace):
            return False
        return ((self.start, self.stop, self.num) ==
                (other.start, other.stop, other.num))
    def __ne__(self, other):
        return not self==other
    def __hash__(self):
        return hash((type(self), self.start, self.stop, self.num))
Chris_Rands
sumber
2

Untuk mengatasi masalah presisi float, Anda dapat menggunakan Decimalmodul .

Ini menuntut upaya ekstra untuk mengubah Decimaldari intatau floatsaat menulis kode, tetapi Anda bisa meneruskan strdan memodifikasi fungsi jika memang dibutuhkan kenyamanan semacam itu.

from decimal import Decimal
from decimal import Decimal as D


def decimal_range(*args):

    zero, one = Decimal('0'), Decimal('1')

    if len(args) == 1:
        start, stop, step = zero, args[0], one
    elif len(args) == 2:
        start, stop, step = args + (one,)
    elif len(args) == 3:
        start, stop, step = args
    else:
        raise ValueError('Expected 1 or 2 arguments, got %s' % len(args))

    if not all([type(arg) == Decimal for arg in (start, stop, step)]):
        raise ValueError('Arguments must be passed as <type: Decimal>')

    # neglect bad cases
    if (start == stop) or (start > stop and step >= zero) or \
                          (start < stop and step <= zero):
        return []

    current = start
    while abs(current) < abs(stop):
        yield current
        current += step

Output sampel -

list(decimal_range(D('2')))
# [Decimal('0'), Decimal('1')]
list(decimal_range(D('2'), D('4.5')))
# [Decimal('2'), Decimal('3'), Decimal('4')]
list(decimal_range(D('2'), D('4.5'), D('0.5')))
# [Decimal('2'), Decimal('2.5'), Decimal('3.0'), Decimal('3.5'), Decimal('4.0')]
list(decimal_range(D('2'), D('4.5'), D('-0.5')))
# []
list(decimal_range(D('2'), D('-4.5'), D('-0.5')))
# [Decimal('2'),
#  Decimal('1.5'),
#  Decimal('1.0'),
#  Decimal('0.5'),
#  Decimal('0.0'),
#  Decimal('-0.5'),
#  Decimal('-1.0'),
#  Decimal('-1.5'),
#  Decimal('-2.0'),
#  Decimal('-2.5'),
#  Decimal('-3.0'),
#  Decimal('-3.5'),
#  Decimal('-4.0')]
shad0w_wa1k3r
sumber
2
Dengan Decimalinput yang serupa , np.arangebekerja sama:np.arange(Decimal('-2.0'), Decimal('2.0'), Decimal('0.1'))
hpaulj
2
Ya terima kasih Meskipun, itu akan membutuhkan lib eksternal (numpy).
shad0w_wa1k3r
1

Tambahkan koreksi otomatis untuk kemungkinan tanda yang salah pada langkah:

def frange(start,step,stop):
    step *= 2*((stop>start)^(step<0))-1
    return [start+i*step for i in range(int((stop-start)/step))]
Bob
sumber
1

Solusi saya:

def seq(start, stop, step=1, digit=0):
    x = float(start)
    v = []
    while x <= stop:
        v.append(round(x,digit))
        x += step
    return v
Jjen
sumber
1

Solusi Terbaik: tidak ada kesalahan pembulatan
_________________________________________________________________________________

>>> step = .1
>>> N = 10     # number of data points
>>> [ x / pow(step, -1) for x in range(0, N + 1) ]

[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]

_________________________________________________________________________________

Atau, untuk rentang yang ditetapkan alih-alih mengatur titik data (misalnya fungsi berkelanjutan), gunakan:

>>> step = .1
>>> rnge = 1     # NOTE range = 1, i.e. span of data points
>>> N = int(rnge / step
>>> [ x / pow(step,-1) for x in range(0, N + 1) ]

[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]

Untuk mengimplementasikan fungsi: ganti x / pow(step, -1)dengan f( x / pow(step, -1) ), dan tentukan f.
Sebagai contoh:

>>> import math
>>> def f(x):
        return math.sin(x)

>>> step = .1
>>> rnge = 1     # NOTE range = 1, i.e. span of data points
>>> N = int(rnge / step)
>>> [ f( x / pow(step,-1) ) for x in range(0, N + 1) ]

[0.0, 0.09983341664682815, 0.19866933079506122, 0.29552020666133955, 0.3894183423086505, 
 0.479425538604203, 0.5646424733950354, 0.644217687237691, 0.7173560908995228,
 0.7833269096274834, 0.8414709848078965]
Jason
sumber
1

start dan stop bersifat inklusif dan bukan yang satu (biasanya stop dikecualikan) dan tanpa impor, dan menggunakan generator

def rangef(start, stop, step, fround=5):
    """
    Yields sequence of numbers from start (inclusive) to stop (inclusive)
    by step (increment) with rounding set to n digits.

    :param start: start of sequence
    :param stop: end of sequence
    :param step: int or float increment (e.g. 1 or 0.001)
    :param fround: float rounding, n decimal places
    :return:
    """
    try:
        i = 0
        while stop >= start and step > 0:
            if i==0:
                yield start
            elif start >= stop:
                yield stop
            elif start < stop:
                if start == 0:
                    yield 0
                if start != 0:
                    yield start
            i += 1
            start += step
            start = round(start, fround)
        else:
            pass
    except TypeError as e:
        yield "type-error({})".format(e)
    else:
        pass


# passing
print(list(rangef(-100.0,10.0,1)))
print(list(rangef(-100,0,0.5)))
print(list(rangef(-1,1,0.2)))
print(list(rangef(-1,1,0.1)))
print(list(rangef(-1,1,0.05)))
print(list(rangef(-1,1,0.02)))
print(list(rangef(-1,1,0.01)))
print(list(rangef(-1,1,0.005)))
# failing: type-error:
print(list(rangef("1","10","1")))
print(list(rangef(1,10,"1")))

Python 3.6.2 (v3.6.2: 5fd33b5, 8 Jul 2017, 04:57:36) [MSC v.1900 64 bit (AMD64)]

Goran B.
sumber
1

Saya tahu saya terlambat ke pesta di sini, tapi inilah solusi generator sepele yang bekerja di 3.6:

def floatRange(*args):
    start, step = 0, 1
    if len(args) == 1:
        stop = args[0]
    elif len(args) == 2:
        start, stop = args[0], args[1]
    elif len(args) == 3:
        start, stop, step = args[0], args[1], args[2]
    else:
        raise TypeError("floatRange accepts 1, 2, or 3 arguments. ({0} given)".format(len(args)))
    for num in start, step, stop:
        if not isinstance(num, (int, float)):
            raise TypeError("floatRange only accepts float and integer arguments. ({0} : {1} given)".format(type(num), str(num)))
    for x in range(int((stop-start)/step)):
        yield start + (x * step)
    return

maka Anda dapat memanggilnya seperti aslinya range()... tidak ada penanganan kesalahan, tetapi beri tahu saya jika ada kesalahan yang dapat ditangkap secara wajar, dan saya akan memperbarui. atau Anda dapat memperbaruinya. ini adalah StackOverflow.

David Culbreth
sumber
0

Berikut adalah solusi saya yang berfungsi baik dengan float_range (-1, 0, 0,01) dan berfungsi tanpa kesalahan representasi floating point. Ini tidak terlalu cepat, tetapi berfungsi dengan baik:

from decimal import Decimal

def get_multiplier(_from, _to, step):
    digits = []
    for number in [_from, _to, step]:
        pre = Decimal(str(number)) % 1
        digit = len(str(pre)) - 2
        digits.append(digit)
    max_digits = max(digits)
    return float(10 ** (max_digits))


def float_range(_from, _to, step, include=False):
    """Generates a range list of floating point values over the Range [start, stop]
       with step size step
       include=True - allows to include right value to if possible
       !! Works fine with floating point representation !!
    """
    mult = get_multiplier(_from, _to, step)
    # print mult
    int_from = int(round(_from * mult))
    int_to = int(round(_to * mult))
    int_step = int(round(step * mult))
    # print int_from,int_to,int_step
    if include:
        result = range(int_from, int_to + int_step, int_step)
        result = [r for r in result if r <= int_to]
    else:
        result = range(int_from, int_to, int_step)
    # print result
    float_result = [r / mult for r in result]
    return float_result


print float_range(-1, 0, 0.01,include=False)

assert float_range(1.01, 2.06, 5.05 % 1, True) ==\
[1.01, 1.06, 1.11, 1.16, 1.21, 1.26, 1.31, 1.36, 1.41, 1.46, 1.51, 1.56, 1.61, 1.66, 1.71, 1.76, 1.81, 1.86, 1.91, 1.96, 2.01, 2.06]

assert float_range(1.01, 2.06, 5.05 % 1, False)==\
[1.01, 1.06, 1.11, 1.16, 1.21, 1.26, 1.31, 1.36, 1.41, 1.46, 1.51, 1.56, 1.61, 1.66, 1.71, 1.76, 1.81, 1.86, 1.91, 1.96, 2.01]
pymen
sumber
0

Saya hanya pemula, tetapi saya memiliki masalah yang sama, ketika mensimulasikan beberapa perhitungan. Inilah cara saya mencoba menyelesaikannya, yang tampaknya bekerja dengan langkah desimal.

Saya juga cukup malas dan jadi saya merasa sulit untuk menulis fungsi jangkauan saya sendiri.

Pada dasarnya apa yang saya lakukan adalah mengubah xrange(0.0, 1.0, 0.01)ke saya xrange(0, 100, 1)dan menggunakan pembagian dengan 100.0di dalam loop. Saya juga khawatir, apakah akan ada kesalahan pembulatan. Jadi saya memutuskan untuk menguji, apakah ada. Sekarang saya dengar, kalau misalnya 0.01dari perhitungan tidak persis float0.01 membandingkan mereka harus mengembalikan False (jika saya salah, tolong beri tahu saya).

Jadi saya memutuskan untuk menguji apakah solusi saya akan bekerja untuk kisaran saya dengan menjalankan tes singkat:

for d100 in xrange(0, 100, 1):
    d = d100 / 100.0
    fl = float("0.00"[:4 - len(str(d100))] + str(d100))
    print d, "=", fl , d == fl

Dan itu dicetak Benar untuk masing-masing.

Sekarang, jika saya benar-benar salah, tolong beri tahu saya.

pengguna2836437
sumber
0

Liner satu ini tidak akan mengacaukan kode Anda. Tanda parameter langkah penting.

def frange(start, stop, step):
    return [x*step+start for x in range(0,round(abs((stop-start)/step)+0.5001),
        int((stop-start)/step<0)*-2+1)]
Tjaart
sumber