我正在尝试解决与个人项目有关的任务的极端情况。
这是确定服务的单价,由total_amount
和cost
组成。
示例包括:
# 1
unit_price = 300 / 1000 # = 0.3
# 2
unit_price = 600 / 800 # = 0.75
# 3
unit_price = 500 / 1600 # = 0.3125
对于1和2,单价可以保持不变。对于3,四舍五入到小数点后2位就足够了,例如(500 / 1600).round(2)
浮点数变长时会出现问题:
# 4
unit_price = 400 / 56000 # = 0.007142857142857143
显而易见的是,浮标相当长。在这种情况下,目标是舍入到第一个有效数字。
我已经考虑过使用正则表达式来匹配第一个非零小数,或者找到第二个部分的长度并应用一些逻辑:
unit_price.match ~= /[^.0]/
unit_price.to_s.split('.').last.size
任何帮助都将受到欢迎
答案 0 :(得分:3)
人们应该使用BigDecimal
进行这种计算。
require 'bigdecimal'
bd = BigDecimal((400.0 / 56000).to_s)
#⇒ 0.7142857142857143e-2
bd.exponent
#⇒ -2
示例:
[10_000.0 / 1_000, 300.0 / 1_000, 600.0 / 800,
500.0 / 1_600, 400.0 / 56_000].
map { |bd| BigDecimal(bd.to_s) }.
map do |bd|
additional = bd.exponent >= 0 ? 0 : bd.exponent + 1
bd.round(2 - additional) # THIS
end.
map(&:to_f)
#⇒ [10.0, 0.3, 0.75, 0.31, 0.007]
答案 1 :(得分:1)
您可以使用正则表达式检测零字符串的长度。有点丑陋,但是可以用:
def significant_round(number, places)
match = number.to_s.match(/\.(0+)/)
return number unless match
zeros = number.to_s.match(/\.(0+)/)[1].size
number.round(zeros+places)
end
pry(main)> significant_round(3.14, 1)
=> 3.14
pry(main)> significant_round(3.00014, 1)
=> 3.0001
答案 2 :(得分:0)
def my_round(f)
int = f.to_i
f -= int
coeff, exp = ("%e" % f).split('e')
"#{coeff.to_f.round}e#{exp}".to_f + int
end
my_round(0.3125)
#=> 0.3
my_round(-0.3125)
#=> -0.3
my_round(0.0003625)
#=> 0.0004
my_round(-0.0003625)
#=> -0.0004
my_round(42.0031)
#=> 42.003
my_round(-42.0031)
#=> -42.003
步骤如下。
f = -42.0031
int = f.to_i
#=> -42
f -= int
#=> -0.0031000000000034333
s = "%e" % f
#=> "-3.100000e-03"
coeff, exp = s.split('e')
#=> ["-3.100000", "-03"]
c = coeff.to_f.round
#=> -3
d = "#{c}e#{exp}"
#=> "-3e-03"
e = d.to_f
#=> -0.003
e + int
#=> -42.003
要改为只舍入四舍五入后的最高有效位,请将方法更改为以下内容。
def my_round(f)
coeff, exp = ("%e" % f).split('e')
"#{coeff.to_f.round}e#{exp}".to_f
end
如果f <= 0
返回与以前的方法相同的结果。这是f > 0
时的示例:
my_round(-42.0031)
#=> -40.0