今更理解する浮動小数点

自分なりのまとめです。間違いがありましたらすみません。

コンピュータでの数の表現の仕方

コンピュータのハードウェア(基盤のようなものを想像してみてください)はICで構成されておりこのICは電圧のONとOFFのどちらかの状態しか持つことが出来ません。
そのためこのICの集まりで何かを表現するためにはONとOFFの集まりで表現するしかありません。そこでONを数字の1、OFFを数字の0としてみなしてこの並びでデータを表現することにします。
そのような0と1の並びをどのように解釈するかで表現したいものが決まります。
101100101.....のようなデータを画像と解釈するか動画と解釈するかゲームと解釈するかそれともただの数として解釈するかは自由です。
ただ、解釈した結果があなたの望むものであれば良いのです。そしてコンピュータが数を扱う際にはこの0と1の並びをただの数として解釈します。

2進数

数ですから数えられなければいけません。しかも、0と1しか使えません。
では、りんごを数えましょう。
まずりんごが何もない状態を想像してください。これを0と1だけで表現しなければいけません。
ではりんごが何もない状態を0と表現することに勝手に決めましょう。
次にさっきの空の状態からりんごを1つ追加しました。さてこれを数えましょう。
この状態を1と表現することに勝手に決めました。
ここからさらにりんごを1つ追加しました。これを数えたいです。
では、これを2と表現しま。。。おっと人間っぽくなってしまいました。
先にお話したようにコンピュータは0と1しか使えないんでしたね。
では、0と1だけでどのように今の状態を表現しましょうか。もう0という表現を使ってしまいました。1という表現もです。
うーん、困りました。どうやら今のままではここまでしか数えられないようです。どうしましょう?
そこで思いつきました。もうひと桁増やして表現しよう。
先ほどまではひと桁でしたが増やすと使える表現が00や01や10や11と増えました。
これでもう一度最初から数えてみましょう。
空の状態を00とする。そこから1つ追加したものを01、そこからさらに1つ追加したものを10、さらに追加したものを11とします。
さっきより数えられる範囲が広がりました。このように桁を増やせばどんどん数えられる範囲が広がります。
このように数を表現する方法が2進数です。0と1しか使わない。0と1は電圧のONとOFFに対応させることができます。そのためこの表現の仕方はコンピュータにとってとても扱いやすいのです。

2進数での負の数

2進数で負の数はどのように表現するのかというといくつか方法があります。
代表的なのでいうと符号をつけるという先頭1桁目が1なら負で0なら正の数とみなすという方法や2の補数という方法など。。。
ここでは負の数も表す方法があるんだなという理解で良いです。

2進数での四則演算

すみません。大切だとは思いますが省略します。調べればすぐに出てくると思いますので。。。
ここでは2進数でも足し算もかけ算も出来るんだとご理解頂ければと思います。

2進数での小数表現

まず10進数で普通に考えてみましょう。1から0.1を得るにはどうすれば良いでしょうか?1×{\frac{1}{10}}とやれば得られますね。
この時1の桁が右に1個ずれてますね。10個で桁が増えるのが10進数ですから10倍すると桁が左にずれ、{\frac{1}{10}}すると桁が右にずれます。
同じように2進数で考えて2進数の1の桁を右に1個ずらすには1×{\frac{1}{2}}とすると1の桁が右にずれて0.1となります。
これらから10進数での0.1は10個集まると桁が上がりますので10進数では0.1です。2進数での0.1は2個集まると桁があがりますので10進数に直すと0.5になります。
このように0.1という同じ見た目でもそれが10進数での表現か2進数での表現かで実際に意味する数が変わります。
では、2進数での0.11を10進数に直した場合の数はいくらでしょうか?これは2進数での0.1と2進数での0.01の組み合わせです。
先ほどのことから2進数での0.1は10進数での0.5でした。では2進数での0.01は10進数ではいくらでしょうか?2進数の0.01を2個集めると桁があがって2進数での0.1になるのです。
つまり 2進数での0.1は10進数での0.5なので10進数での計算に直すと0.5 = 2 × ? です。?は0.25です。つまり2進数の0.01を10進数に直すと0.25です。
このことから2進数での0.11を10進数に直すと0.5+0.25=0.75であることが分かります。
さて、10進数の0.5を2進数で表すと0.1と表せることは分かりました。しかし0.1という表現をそのままコンピュータで使うことは出来ません。はじめに言った通りコンピュータは0と1の並びでしか表現できません。小数点(.)なんてありませんから。ではどのようにコンピュータで表すのでしょうか?

固定小数点表記

小数を表す一つのやり方は小数点の位置を決めておく方法で固定小数点表記と呼ばれます。
例えば、先頭から1桁を整数部分、次の2桁目を小数部分と決めておきます。つまり小数点(.)は1桁目と2桁目の間だと分かります。すると01という0と1だけの表現を0.1であると判断できます。
ただこの方法は精度に問題があります。10進数で表すと例えば、先頭から5桁を整数部分、次の5桁目を小数部分と決めていた場合に3.14159265という小数は0埋めされ00003.141592となってしまい精度が失われてしまいます。そのためあまり精度がいらない数値計算では用いられるという感じでしょうか。

浮動小数点表記

浮動小数点表記の考え方は小数を整数部分が1桁(ただし0ではない)になるように小数点を移動し、x.yyyyy... × 2 ^zという形式で表そうというものです。その時にどうやってこの表現を0と1だけで表そうかと考えた時に3つの部分に分けて表記しようとなりました。
3つとは、符号部、小数部(仮数部とも言います)、指数部です。
符号部というのは2進数での負の数のところで述べた符号をつけるというものです。先頭1桁を0なら正の数、1なら負の数とみなします。つまり浮動小数点表記は負の小数も扱えます。
小数部というのは先ほどの2進数の0.11では浮動小数点の考え方では1.1×2^-1となるので小数部は1です。
指数部はこの場合は2^-1なので-1ですね。
固定小数点表記とは違い小数点の位置は指数部によって後から決まります。

IEEE 754 浮動小数点規格

符号部、小数部、指数部という3つの部分に分けて表現するのは分かったのですが、その3つの部分の並び順は?それぞれの部分は何ビット(0か1の一桁のことを1ビットと言います)になるの?と疑問に思うのですがそれを決めた規格の1つで広く普及しているのがIEEE 754 浮動小数点規格です。単精度浮動小数点規格と倍精度浮動小数点規格の2つの形式が定められています。
ここでは2進数の0.011を単精度浮動小数点規格で表現してみたいと思います。
単精度浮動小数点規格は小数を32ビットで表現します。つまり0か1のどちらかが32個並びます。
符号に先頭1ビット、次の8ビットが指数、次の23ビットが小数部です。
まず正の小数なので先頭1ビットは0ですね。
指数部は少し複雑で「-2」なので「-2」にバイアスの「127」を加算します。指数部にバイアスを加算することで、負の数を含む表現が正の数だけで表現できます。
(-2)+127 = 125 この125を2進数で表して01111101になります。
0.011を正規化すると1.1×2^-2です。小数部には小数点以下の値が入ります。小数第1位から順に「上位ビット→下位ビット」の順で並べるだけです。足りない部分は0で埋めます。
よって以下の画像のようになります。
00111110110000000000000000000000
https://medium-company.com/wp-content/uploads/2021/08/ieee754_3.png
このような形式で2進数の0.011、10進数に直すと0.375という小数は画像のような0と1の並びで表現されます。
倍精度浮動小数点規格は32ビットではなく64ビットで表します。

誤差

10進数での0.1は有限小数だが10進数での0.1を2進数の小数で表そうとすると無限小数になってしまいます。無限はまずいので32ビットや64ビットで収まるようにどこかで切って丸めるしかありません。そのため誤差が出ることが多いです。
IEEE 754 浮動小数点規格では10進数の小数を2進数の小数で表して正規化(1.1×2^-2のような形)した時の小数部の23桁目と24桁目が00 → 00、01 → 00、10 → 10、11 → 100として丸めます。これを最近接偶数方向丸めといいます。

commandパターン

クラスのメソッド呼び出しとその具体的な振る舞いを分離することを目的とする。

class AttackSordCommand
  def do
    attacker = AttackerSord.new
    attacker.attack
  end
end
class DefenderShieldCommand
  def do
    defender = DeffenderShield .new
    defender.defence
  end
end
class Yusha

  def initialize(attack_command, defence_command)
    @attack_command = attack_command
    @defence_command = defence_command
  end

  def kogeki
    @attack_command.do
  end

  def bogyo
    @defence_command.do
  end
end

勇者クラスは攻撃した際にどんなことが起こるのか知りません。
その具体的なことはAttackCommandが知っています。
防御した際も同様です。
剣で攻撃だったのが槍で攻撃に代わっても勇者クラスは変更しなくてすみます。
今まで

yusha = Yusha.new(AttackSordCommand.new, DefenderShieldCommand)

だったのが
槍コマンドクラスが追加されて

class AttackSpearCommand
  def do
    attacker = AttackerSpear.new
    attacker.attack
  end
end

以下のように勇者クラスの生成方法が変わるだけです。

yusha = Yusha.new(AttackSpearCommand.new, DefenderShieldCommand)

stateパターン

状態を表すクラス

例えば活性状態(Activate)、非活性状態(Deactivate)があるとする。

その時の振る舞いをそのクラスに実装する

class Activate
  def get_color
    "yellow"
  end

  def change_state(is_avtive)
    return self if is_active

   Deactive.new
  end

end
class Deactivate
  def get_color
    "grey"
  end

  def change_state(is_avtive)
    return self if !is_active

   Active.new
  end
end
class Button
  def initialize
    @is_avtive = true
    @state = Deactivate.new
  end

  def color
    @state.get_color
  end

  def click
    @state = @state.change_state(!@is_active)
  end
end

Ruby オブジェクト指向 メモ

オブジェクト指向で意識すること

  • インスタンス変数はアクセサメソッドで提供するようにする。
  • クラスの中で他のクラスをnewするのは出来るだけ避ける。
  • 依存関係の逆転

インスタンス変数はアクセサメソッドで提供するようにする。

class Human
    def initialize(name)
        @name = name
    end
    
    def name
        @name
    end
end

hoge = Human.new('tarou')

puts hoge.name

上のようにアクセサで提供する。いろんな場所でhuman.nameと使われているときに例えばnameメソッドだけ変更 すればよくて済む。rubyならattr_reader使えばよい。

クラスの中で他のクラスをnewするのは出来るだけ避ける。

あるオブジェクトの中でnewするとそのクラスと強い結合が生まれてしまう。 出来ることなら依存オブジェクトの注入(DI)をする。これをすることで注入されたオブジェクトが何であるかには依存しなくなり、注入されたオブジェクトのインターフェースにのみ依存するようになる。ただ、色々理由でDI出来ないこともよくある。その場合は以下のように出来るだけ隔離し依存を明らかにしておく。

class Gear
    def initialize(chainring, cog, rim, tire)
        @chainring = chainring
        @cog = cog
        @rim = rim
        @tire = tire
    end

    def gear_inches
        ratio*wheel.diameter
    end

    def wheel
        @wheel ||= Wheel.new(rim, tire)
    end
end

ここからさらにgear_inchesメソッドが複雑化した場合は

def gear_inches
        #ここがかなり複雑化する
        ratio*diameter
end

def diameter
      @wheel.diameter
 end

このように外部メッセージを隔離し、依存を明らかにしておく。 この変更により外部メッセージである@wheel.diameterに依存していたgear_inchesは 自身のメソッドであるdiameterに依存するようになり、外部への依存を取り除けた。 仮にwheelクラスがdiameterメソッドの名前を変更したりした場合でも副作用はこのシンプルな ラッパーメソッドだけで済む。この副作用を駆逐するために最初から問題を避ける依存関係逆転というテクがある。

依存関係の逆転

class Gear
    attr_reader :chainring, :cog
    def initialize(chainring, cog)
        @chainring = chainring
        @cog = cog
    end

    def gear_inches(diameter)
        ratio*diameter
    end

    def ratio
        chainring / cog.to_f
    end

end

class Weel
    attr_reader :rim, :tire, :gear
    def initialize(rim, tire, chainring, cog)
        @rim = rim
        @tire = tire
        @gear = Gear.new(chainring, cog)
    end

    def diameter
        rim + (tire*2)
    end

    def gear_inches
        gear.gear_inches(diameter)
    end
end

このように依存関係を逆転させる際はより安定したクラスに依存するようにする。 抽象的なクラスまたは、他とあまり結合していないクラスに依存させる。

デメテルの法則

必ずしも守る必要はない。 クラス間の結合を減らすテクニック。 たくさんドットでつなげるとその通り道のクラス全てと結合して再利用が難しいクラスになってしまう。

class LawOfDemeterExample
    def initialize
        @another_class = AnotherClass.new
    end
    #自身が直接アクセスできるメソッドはOK
    def sample_method_following_demeter(obj)
        my_method#オブジェクトO自身のメソッドは呼び出すことができる
        data1 = @another_class.get_data#呼び出し用のメソッドまたは属性として同じクラス内で宣言されたオブジェクトのメソッドは呼び出すことができる
        data2 = obj.get_data#メソッド「sample_method_following_demeter」の引数のメソッドは呼び出すことができる
    end

    def my_method
    end
end

class AnotherClass
    def get_data
    end
end

O = LawOfDemeterExample.new

メソッドsample_method_following_demeterをもつクラスLawOfDemeterExampleのインスタンスをOとすると オブジェクト指向プログラミングにデメテルの法則を適用すると、次のように解釈できる。

  • オブジェクトO自身のメソッドは呼び出すことができる
  • メソッド「sample_method_following_demeter」の引数のメソッドは呼び出すことができる
  • メソッド「sample_method_following_demeter」の内部で生成/初期化したオブジェクトのメソッドは呼び出すことができる
  • 呼び出し用のメソッドまたは属性として同じクラス内で宣言されたオブジェクトのメソッドは呼び出すことができる
  • オブジェクトOがアクセスできるメソッド「sample_method_following_demeter」のスコープにある全域オブジェクトのメソッドは呼び出すことができる

JSで意識すること メモ ES6

JSで意識すること

自分用のJS扱うときに意識することメモ

  • 参照のコピーとthis
  • list2
  • list3

  • JSで意識すること
    • 参照のコピーとthis
      • 見出し1-1
      • 見出し1-2
      • 見出し1-3
    • 非同期Promiseチェーン
      • 見出し2-1
    • 見出し3
      • 見出し3-1
    • 参考リンク
    • 関連リンク

続きを読む

rubyでデザインパターン strategyパターン

strategy(戦略)パターン
戦略を切り替えるパターンです。
処理を他のクラスに委譲することを前提とし、クライアント(この例ではsorterです。)が自由にそのクラスを切り替えることが出来ます。

strategyとtemplate methodパターンの違いはこんな説明がわかりやすかったです。
Strategyの方は,全く異なるアルゴリズムごっそり差し替えるためのパターン。
一方でTemplate Methodの方は"template method"の名の通り,アルゴリズムの実行ステップを手順として規定しておいて,各ステップ内の処理をサブクラスに実装させている. Template methodは将来の実装のためのガイドとも言える。 なお実行ステップを記述するtemplate methodは当然overrideされないようにしておく.
また、継承使ったらtemplate_methodで委譲したらstrategyパターンみたいな解説もあるのでちょっと曖昧。

class SortStrategy
    def sort(dataset)
    end
end

class BubbleSortStrategy < SortStrategy
    def sort(dataset)
        puts "バブルソートを実行"
        dataset
    end
end

class QuickSortStrategy < SortStrategy
    def sort(dataset)
        puts "クイックソートを実行"
        dataset
    end
end

class Sorter
    attr_reader :sorter
    
    def initialize(sorter)
        @sorter = sorter
    end
    
    def sort(dataset)
        @sorter.sort(dataset)
    end
    
end

dataset = [1,4,5,6,6,7]
sorter = Sorter.new(BubbleSortStrategy.new)
sorter.sort(dataset)
sorter = Sorter.new(QuickSortStrategy.new)
sorter.sort(dataset)

rubyでデザインパターン builderパターン

砂糖水を作る(
ビルダ (Builder) | Ruby デザインパターン | 酒と涙とRubyとRailsと
)
こちらの写生です。

操作と手順を分離するパターンです。
factoryパターンとの大きな違いは、factoryパターンは作成が1ステップでできる場合に使うのに対し、builderパターンは作成に複数のステップを踏む場合に使う点。

class SugarWater
    attr_accessor :water, :sugar
    
    def initialize(water, sugar)
        @water = water
        @sugar = sugar
    end
    
end

class SugarWaterBuilder
    def initialize
        @sugar_water = SugarWater.new(0,0)
    end
    
    def add_sugar(sugar_amount)
        @sugar_water.sugar += sugar_amount
    end
    
    def add_water(water_amount)
        @sugar_water.water += water_amount
    end
    
    def result
        @sugar_water
    end
end

class Director
    def initialize(builder)
        @builder = builder
    end
    
    def cook
        @builder.add_water(150)
        @builder.add_sugar(90)
        @builder.add_water(300)
        @builder.add_sugar(35)
    end
end

builder = SugarWaterBuilder.new
director = Director.new(builder)
director.cook
p builder.result






####################別の例#####################
class Burger
    attr_reader :size, :cheese, :lettuce, :tomato, :pepperoni
    
    def initialize(builder)
        @size = builder.size
        @cheese = builder.cheese
        @lettuce = builder.lettuce
        @tomato = builder.tomato
    end
end

class BurgerBuilder
    attr_reader :size, :cheese, :lettuce, :tomato, :pepperoni
    
    def initialize(size)
        @size = size
    end
    
    def addPepperoni
        @pepperoni = true
    end
    
    def addLettuce
        @lettuce = true
    end
    
    def addCheese
        @cheese = true
    end
    
    def addTomato
        @tomato = true
    end
    
    def build
        Burger.new(self)
    end
end

burgar = BurgerBuilder.new(14)
burgar.addTomato
burgar.addCheese
tomato_cheese_burgar = burgar.build
p tomato_cheese_burgar