首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何有效地擦除Ruby的负零浮点数?

如何有效地擦除Ruby的负零浮点数?
EN

Stack Overflow用户
提问于 2012-01-03 18:45:32
回答 5查看 3.2K关注 0票数 16

在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
EN

回答 5

Stack Overflow用户

回答已采纳

发布于 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)
票数 12
EN

Stack Overflow用户

发布于 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

实际上,我并不比我接受的方案更喜欢这个方案,因为它缺乏清晰度。如果我在一段不是我自己写的代码中看到了这一点,我想知道为什么你会想把所有的东西都加零。

它确实解决了这个问题,所以我想我还是在这里分享吧。

票数 13
EN

Stack Overflow用户

发布于 2012-01-03 19:14:18

我想不出比这更好的了:

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"]
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8711055

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档