16

在红宝石中,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 的一部分,但文档显示它用作 Comparable 与<=>运算符的一部分。另外,我宁愿为此目的测试零条件,因为它看起来更简单。

而且,尽管 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'

这将使处理浮点数组更容易:

[-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 中它比@the Tin Man 的要快一些

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 回答