質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.59%

float型でも取り扱えるようにrange関数のような関数を作る

解決済

回答 6

投稿

  • 評価
  • クリップ 0
  • VIEW 542

score 46

前提・実現したいこと

range関数がfloat型に対応していないので、
関数を定義したが、stepを0.1にすると
すごく細かい少数が出てきてしまう。
これは、2進法のためというのも何となく理解できました。

何か良い方法はないものでしょうか?

発生している問題・エラーメッセージ

In:list(drange(17.8,20,0.1))
Out: 
[17.8,
 17.900000000000002,
 18.000000000000004,
 18.100000000000005,
 18.200000000000006,
 18.300000000000008,
 18.40000000000001,
 18.50000000000001,
 18.600000000000012,
 18.700000000000014,
 18.800000000000015,
 18.900000000000016,
 19.000000000000018,
 19.10000000000002,
 19.20000000000002,
 19.300000000000022,
 19.400000000000023,
 19.500000000000025,
 19.600000000000026,
 19.700000000000028,
 19.80000000000003]

該当のソースコード

def drange(start, end, step):
    n =start
    while n + step < end:
          yield n
          n += step

試したこと

補足情報(FW/ツールのバージョンなど)

python3 

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 6

+5

numpyを使える環境ならnp.arangeがあるので、車輪の再開発をやる必要はありません。

>>> import numpy as np
>>> np.arange(17.8,20,0.1)
array([17.8, 17.9, 18. , 18.1, 18.2, 18.3, 18.4, 18.5, 18.6, 18.7, 18.8,
       18.9, 19. , 19.1, 19.2, 19.3, 19.4, 19.5, 19.6, 19.7, 19.8, 19.9])


ただしnumpy配列は綺麗に表示するために表示桁数を少なくしているので注意。実際には浮動小数点数なので、下の方の桁には誤差がある場合もあります(というかほぼ当然にある)。

自分で作る場合は、毎回stepを足していくのではなく、先に全体に含まれる要素数を計算してからループを要素数分だけ回して処理するか、(同じことですが)整数のカウンタを別途用意してそちらをインクリメントしながらループを回し、(start + count*stepみたいにして)出していった方が誤差が累積しないはずです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/24 09:23

    np.arangeなんていうのがあるのですね、勉強不足です。。。
    誤差の累積を考慮する点も自分では考えてもなかったので、とても参考になりました。
    ありがとうございます。

    キャンセル

+4

小数点刻みを実装しているnumpy.arangeのドキュメントに

https://docs.scipy.org/doc/numpy-1.15.0/reference/generated/numpy.arange.html

When using a non-integer step, such as 0.1, the results will often not be consistent. It is better to use linspace for these cases.

と書いてあって、その用途にはnumpy.linspaceを使うのがいい、とあります。

で、numpy.linspaceの実装はというと、np.linspace(17.8,19.9,22)np.arange(22) * 0.1 + 17.8と同等です。(こことかここ
誤差を積み重ねないために、整数で要素を組み上げてから演算で作ってます。

「range関数がfloat型に対応していない」というよりも、うかつに対応すると誤差を累積したデータを簡単に作れてしまうからデザインとして採用していないのかと思いました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/24 09:33

    ほー!公式にはfloat型の場合は、arangeよりもlinespaceをお勧めしているのですね。
    2つともfloat型にも対応しているが、
    arangeは、毎回stepを足す作り方で、
    linespaceは、(start + count*step)ということでしょうかね。

    それなら、linespeceの方がよさげですね!ありがとうございます。

    キャンセル

+3

美しくはありませんが、分数を使って

from fractions import Fraction

def drange(start, stop, step):
    while start < stop:
        yield float(start)
        start += step

a = Fraction(178, 10)
b = 20
c = Fraction(1, 10)

print(list(drange(a, b, c)))


という手もあります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

見た目だけの問題なら気にするほうがおかしいのでそのまま使う、ということでいいんじゃないでしょうか。
この配列でいかに見た目を気にしたところで、その値を使うときには必ずその数値(あなたの言う細かい小数)になるので意味ありません

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/23 15:39

    単純に0.2(誤差含み)を足していくと、同じ方向に誤差が蓄積していってしまいます。

    キャンセル

  • 2019/01/24 09:27

    そうなんですよね、実際。
    ただ、私の作った関数の場合、誤差の累積も考えられるようなので、
    他の方の指摘にもあるように、それならnp.arangeでいけばいいかなと思っております。
    見た目もきれいにしてくれるようですし(笑)

    キャンセル

checkベストアンサー

+1

もう少し美しくできるようになりました。

from fractions import Fraction

def drange(start, stop, step):
    start = Fraction(str(start))
    step = Fraction(str(step))
    while start < stop:
        yield float(start)
        start += step

print(list(drange(17.8, 20, 0.1)))

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/24 09:45

    これは、きれいにでますね!理想的なlistが作れます。
    ちょっとどうなってるか勉強してみたいと思います。
    大変参考になりました。ありがとうございます。

    キャンセル

+1

Fraction を使った回答がでますが、 Fraction をつかわないならこんな感じにすると
float の足し算の誤差の集積を防ぐことができます。
(stop 値付近の値がループにあらわれるか否かの判定はちょっと不安定かもしれません。)

def drange(start, stop, step = 1.0):
    count = int((stop - start) / step + 2)
    for t in range(0, count):
        v = start + t * step
        if v < stop:
            yield v

print(list(drange(17.8, 20, 0.1)))

print(list(drange(0.0, 0.5, 0.1)))
print(list(drange(10000.0, 10000.5, 0.1)))

実行例
イメージ説明

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.59%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る