在Ruby中,0.0 * -1 == -0.0
。
我有一个应用程序,其中我用-1
乘以一堆Float
对象,但我不喜欢输出中的-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.0
。
示例:
clean_output(-0.0) #=> 0.0
clean_output(-3.0) #=> -3.0
clean_output(3.0) #=> 3.0
发布于 2012-01-04 00:40:51
如果你写的代码让你感到困惑,那么这应该真的会让你大吃一惊:
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的一部分,但文档显示它与<=>
运算符一起使用作为比较的一部分。另外,出于这个目的,我更愿意测试零条件,因为它看起来更简单。
而且,尽管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-03 21:04:35
实际上有一个不需要条件的解决方案。
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-03 19:14:18
我想不出比这更好的了:
def clean_output(value)
value.nonzero? || value.abs
end
但这只是您的解决方案的一个变体。不过,与您的不同,这个函数不会改变value
的类型(例如,如果您传递-0
,它将返回0
)。但看起来这对你来说并不重要。
如果您确信这将使您代码更加整洁,那么您可以将类似的方法添加到Numeric
类(这将使该方法可用于Float
、Fixnum
和其他数值类):
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"]
https://stackoverflow.com/questions/8711055
复制相似问题