16

Rubyでは、0.0 * -1 == -0.0

たくさんのFloatオブジェクトに。を掛けるアプリケーションがありますが、混乱するため、出力-1のは好きではありません。-0.0

代わりにFloat#to_s出力を作成するスマートな方法はありますか?0.0-0.0

Floatある種のスクラバー/ヘルパーメソッドを使用してすべてのオブジェクトを実行することはまったく問題ありませんが、次のことは私をさらに混乱させる傾向があります。

def clean_output(amount)
  if amount.zero?
    0.0
  else
    amount
  end
end

アップデート:

私が探しているものをより正確に言うと、フロートの束全体で実行できるソリューションが必要です。フロートの中には、ネガティブなものもあれば、ポジティブなものもあります。負のゼロが負のゼロでない限り、負の値は負のままである必要があります-0.0

例:

clean_output(-0.0) #=>  0.0
clean_output(-3.0) #=> -3.0
clean_output(3.0)  #=>  3.0
4

5 に答える 5

13

実際には、条件を必要としない解決策があります。

def clean_output(value)
  value + 0
end

出力:

> clean_output(3.0)
=> 3.0 
> clean_output(-3.0)
=> -3.0 
> clean_output(-0.0)
=> 0.0

明確さが不足しているため、私が受け入れたソリューションよりもこのソリューションの方が実際には好きではありません。自分で書いたのではないコードでこれを見るとしたら、なぜすべてにゼロを追加したいのだろうかと思います。

それでも問題は解決するので、とにかくここで共有したいと思いました。

于 2012-01-03T13:04:34.773 に答える
12

あなたが書いたコードがあなたを混乱させるなら、これは本当にあなたの心を曲げるべきです:

def clean_output(amount)
  amount.zero? && 0.0 || amount
end

いくつかの証拠があります:

irb(main):005:0> f = 0.0
=> 0.0
irb(main):006:0> f.zero? && 0.0 || f
=> 0.0
irb(main):007:0> f = -0.0
=> -0.0
irb(main):008:0> f.zero? && 0.0 || f
=> 0.0
irb(main):009:0> f=1.0
=> 1.0
irb(main):010:0> f.zero? && 0.0 || f
=> 1.0

nonzero?ユースケースが少し混乱しているので、私は使用するのが好きではありません。これはNumericの一部ですが、ドキュメントには、Comparablewithoperatorの一部として使用されていることが示されてい<=>ます。さらに、この目的ではゼロ条件をテストしたいのですが、それはもっと簡単に思えるからです。

そして、OPのコードは冗長に見えるかもしれませんが、これは時期尚早の最適化が報われない別のケースです。

require 'benchmark'

def clean_output(amount)
  if amount.zero?
    0.0
  else
    amount
  end
end

def clean_output2(amount)
  amount.zero? && 0.0 || amount
end

def clean_output3(value)
  value + 0
end

class Numeric
  def clean_to_s
    (nonzero? || abs).to_s
  end
end


n = 5_000_000
Benchmark.bm(14) do |x|
  x.report( "clean_output:"  ) { n.times { a = clean_output(-0.0)  } }
  x.report( "clean_output2:" ) { n.times { a = clean_output2(-0.0) } }
  x.report( "clean_output3:" ) { n.times { a = clean_output3(-0.0) } }
  x.report( "clean_to_s:"    ) { n.times { a = 0.0.clean_to_s      } }
end

そして結果:

ruby test.rb 
                    user     system      total        real
clean_output:   2.120000   0.000000   2.120000 (  2.127556)
clean_output2:  2.230000   0.000000   2.230000 (  2.222796)
clean_output3:  2.530000   0.000000   2.530000 (  2.534189)
clean_to_s:     7.200000   0.010000   7.210000 (  7.200648)

ruby test.rb 
                    user     system      total        real
clean_output:   2.120000   0.000000   2.120000 (  2.122890)
clean_output2:  2.200000   0.000000   2.200000 (  2.203456)
clean_output3:  2.540000   0.000000   2.540000 (  2.533085)
clean_to_s:     7.200000   0.010000   7.210000 (  7.204332)

なしのバージョンを追加しましたto_s。これらは数年前の私のラップトップで実行されたため、結果の時間は以前のテストよりも長くなります。

require 'benchmark'

def clean_output(amount)
  if amount.zero?
    0.0
  else
    amount
  end
end

def clean_output2(amount)
  amount.zero? && 0.0 || amount
end

def clean_output3(value)
  value + 0
end

class Numeric
  def clean_to_s
    (nonzero? || abs).to_s
  end

  def clean_no_to_s
    nonzero? || abs
  end

end


n = 5_000_000
Benchmark.bm(14) do |x|
  x.report( "clean_output:"  ) { n.times { a = clean_output(-0.0)  } }
  x.report( "clean_output2:" ) { n.times { a = clean_output2(-0.0) } }
  x.report( "clean_output3:" ) { n.times { a = clean_output3(-0.0) } }
  x.report( "clean_to_s:"    ) { n.times { a = -0.0.clean_to_s     } }
  x.report( "clean_no_to_s:" ) { n.times { a = -0.0.clean_no_to_s  } }
end

そして結果:

ruby test.rb 
                    user     system      total        real
clean_output:   3.030000   0.000000   3.030000 (  3.028541)
clean_output2:  2.990000   0.010000   3.000000 (  2.992095)
clean_output3:  3.610000   0.000000   3.610000 (  3.610988)
clean_to_s:     8.710000   0.010000   8.720000 (  8.718266)
clean_no_to_s:  5.170000   0.000000   5.170000 (  5.170987)

ruby test.rb 
                    user     system      total        real
clean_output:   3.050000   0.000000   3.050000 (  3.050175)
clean_output2:  3.010000   0.010000   3.020000 (  3.004055)
clean_output3:  3.520000   0.000000   3.520000 (  3.525969)
clean_to_s:     8.710000   0.000000   8.710000 (  8.710635)
clean_no_to_s:  5.140000   0.010000   5.150000 (  5.142462)

減速していたものを整理するにはnon_zero?

require 'benchmark'

n = 5_000_000
Benchmark.bm(9) do |x|
  x.report( "nonzero?:" ) { n.times { -0.0.nonzero? } }
  x.report( "abs:"      ) { n.times { -0.0.abs      } }
  x.report( "to_s:"     ) { n.times { -0.0.to_s     } }
end

結果とともに:

ruby test.rb 
               user     system      total        real
nonzero?:  2.750000   0.000000   2.750000 (  2.754931)
abs:       2.570000   0.010000   2.580000 (  2.569420)
to_s:      4.690000   0.000000   4.690000 (  4.687808)

ruby test.rb 
               user     system      total        real
nonzero?:  2.770000   0.000000   2.770000 (  2.767523)
abs:       2.570000   0.010000   2.580000 (  2.569757)
to_s:      4.670000   0.000000   4.670000 (  4.678333)
于 2012-01-03T16:40:50.813 に答える
4

私はそれ以上のものを考えることはできません:

def clean_output(value)
  value.nonzero? || value.abs
end

しかし、それはあなたのソリューションのバリエーションにすぎません。ただし、これとは異なり、これはタイプを変更しませんvalue(たとえば、合格-0した場合は戻ります0)。しかし、あなたの場合は重要ではないようです。

コードがよりクリーンになると確信している場合は、そのようなメソッドをNumericクラスに追加できます(これにより、、、、およびその他の数値クラスでそのメソッドを使用できるようになります)FloatFixnum

class Numeric
  def clean_to_s
    (nonzero? || abs).to_s
  end
end

そしてそれを使用します:

-0.0.clean_to_s # => '0.0'
-3.0.clean_to_s # => '-3.0'
# same method for Fixnum's as a bonus
-0.clean_to_s   # => '0'

これにより、floatの配列の処理が簡単になります。

[-0.0, -3.0, 0.0, -0].map &:clean_to_s
# => ["0.0", "-3.0", "0.0", "0"]
于 2012-01-03T11:14:18.317 に答える
0

答えがゼロかどうかを確認してから、値にabsを適用するだけです。-0.0を0.0に変換します

fl_num = -0.0
fl_num = fl_num.abs

fl_num = 0.0
于 2012-01-03T10:56:02.233 に答える
0

私にとって、このコードの意図は少し明確であり、少なくともRuby 1.9.3では、@ TheTinManよりもわずかに高速です。

def clean_output4(amount)
  amount.zero? ? 0.0 : amount
end

                     user     system      total        real
clean_output:    0.860000   0.000000   0.860000 (  0.859446)
clean_output4:   0.830000   0.000000   0.830000 (  0.837595)
于 2015-11-16T06:32:45.530 に答える