我正在尝试解决问题:
你的任务是写一个需要几米的简单函数, 并使用公制前缀输出它。对于我们想要的练习 大于一米的单位,从米到米,不包括 十进制和百米。传入的所有值都是正数 整数 实例
meters(51500)
# returns "51.5km"
meters(5000000)
# returns "5Mm"
我的代码:
def meters(x)
map_prefix={ 24=>'Y', 21=> 'Z', 18=> 'E', 15=> 'P', 12=> 'T', 9=>'G', 6=>'M', 3=>'k',0=>'' }
digits=(x.to_i.to_s.size-1)/3
division=x/(10.0**(3*digits))
"#{division}#{map_prefix[3*digits]}m".sub(/\.0([^\d])/,'\1')
end
它不适用于meters(56*10**24) #->expected 56Ym ,instead got 56.000000000004Ym
,但适用于较大的数字,例如meters(88*10**24) #->88Ym
。代码通过了50个测试中的49个,有人可以帮我找到错误吗?
答案 0 :(得分:1)
破解代码以使其正常工作的最简单方法似乎是避免使用浮点数,例如:
#!/usr/bin/env ruby
def meters(x)
map_prefix={ 24=>'Y', 21=> 'Z', 18=> 'E', 15=> 'P', 12=> 'T', 9=>'G', 6=>'M', 3=>'k',0$
map_prefix.default = 'Y'
digits = [((x.to_s.size-1)/3)*3, 24].min
division = x.to_s.insert(-digits - 1, '.')
division.sub!(/0+\z/, '')
division.sub!(/\.\z/, '')
"#{division}#{map_prefix[digits]}m"
end
puts meters(51500)
puts meters(5000000)
puts meters(5001)
puts meters(88*10**24)
puts meters(88*10**24 + 1)
puts meters(100)
puts meters(88*10**27)
puts meters(88*10**27 + 1)
结果如:
./ruby.rb
51.5km
5Mm
5.001km
88Ym
88.000000000000000000000001Ym
100m
88000Ym
88000.000000000000000000000001Ym
更严重的是,你需要避免任何字符串(根本不应该转换为字符串)。 你需要任意精度,所以浮动根本不是一个选择。
答案 1 :(得分:0)
我认为您的问题是您乘以10.0
,但您只想处理整数。
以下是你想要的东西。 (我也做了几次风格改变)。
def meters(x)
digits=(x.to_i.to_s.size-1)/3
prefix = prefixes[3*digits]
value = x / (10 ** (3 * digits))
"#{value}#{prefix}m".sub(/\.0([^\d])/,'\1')
end
def prefixes
{
24 => 'Y',
21 => 'Z',
18 => 'E',
15 => 'P',
12 => 'T',
9 => 'G',
6 => 'M',
3 => 'k',
0 => ''
}
end
这至少可以为错误的解决方案提供正确的解决方案。我不能保证它是所有事情的正确解决方案。
我在这里将哈希变成了自己的函数,因为它似乎是静态的。我在评论中提到的其他事情也是如此。
答案 2 :(得分:0)
您可以使用Float#round
将数字四舍五入到某些数字。对于这个问题,3
应该没问题。
"#{division.round(3)}#{map_prefix[3*digits]}m".sub(/\.0([^\d])/,'\1')
# ^^^^^^^^^
问题背后的原因是:Float
可以存储非常大的整数。但是,对于大于某个限制的整数,Float
无法存储精确。对于[IEEE-754双精度]浮点,该限制为2 53 。
答案 3 :(得分:0)
FWIW这条线将改变红宝石的鸭子打字漂浮。 (注意你将 10.0 作为浮动引入。)
division=x/(10.0**(3*digits))
处理大数字时,最好使用内置的BigDecimal类。虽然这绝对不是防错代码,但更清晰,更不容易出错。
require 'bigdecimal'
def meters(x)
b = BigDecimal.new(x).split
"#{b[1]}#{prefix[b[3] - b[1].length]}m"
end
def prefix
{
24 =>'Y', 21 => 'Z', 18 => 'E', 15 => 'P',
12 => 'T', 9 =>'G', 6 =>'M', 3 =>'k',0 =>''
}
end
答案 4 :(得分:0)
UNITS = " kMGTPEZY"
def meters(x)
sx = x.to_s
pwr = sx.size - 1
return sx if pwr < 3
pfx_sz = (pwr < 24) ? (1 + pwr % 3) : pwr - 23
sxs = sx.reverse.to_i.to_s.reverse
sxs = sxs.ljust([sxs.size, pfx_sz].max, '0')
pfx = sxs[0, pfx_sz]
pfx << '.' if (pfx.size < sxs.size)
"#{ pfx }#{ sxs[pfx_sz..-1] }#{ UNITS[[pwr/3, 8].min] }"
end
meters 3 #=> "3"
meters 100 #=> "100"
meters 4000 #=> "4k"
meters 5001 #=> "5.001k"
meters 51500 #=> "51.5k"
meters 5000000 #=> "5M"
meters 88*10**24 #=> "88Y"
meters 88*10**24 + 1 #=> "88.000000000000000000000001Y"
meters 88*10**27 #=> "88000Y"
meters 88*10**27 + 1 #=> "88000.000000000000000000000001Y"