在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
答案 0 :(得分: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
由于缺乏清晰度,我实际上并不比我接受的解决方案更喜欢这种解决方案。如果我在一段代码中看到这个,我没有自己写,我想知道你为什么要为所有东西添加零。
它确实解决了这个问题,所以无论如何我想我会在这里分享它。
答案 1 :(得分: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)
答案 2 :(得分:4)
我想不出比这更好的事情:
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"]
答案 3 :(得分:0)
只需检查答案是否为零,然后将 abs 应用于该值。它会将-0.0转换为0.0
fl_num = -0.0
fl_num = fl_num.abs
fl_num = 0.0
答案 4 :(得分:0)
对我来说,这段代码的意图有点清晰,至少在Ruby 1.9.3中它比@This 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)