如何解析ruby BigDecimal检查?

时间:2015-07-02 07:54:16

标签: ruby bigdecimal

在以下代码中:

x = BigDecimal(10)
s = x.inspect # "#<BigDecimal:6fe4790,'0.1E2',9(36)>"

有没有办法解析s并获得原始值?原因是我有一些使用inspect编写BigDecimal的文本文件,我需要解析这些值。

5 个答案:

答案 0 :(得分:2)

.to_s获取字符串中的值。 .inspect将打印对象

x = BigDecimal(10)
x.to_s
# => "0.1E2"

答案 1 :(得分:1)

"#<BigDecimal:6fe4790,'0.1E2',9(36)>"[/(?<=').+(?=')/]
# => "0.1E2"

答案 2 :(得分:1)

我不知道您使用的是哪个版本的Ruby,所以我检查了some MRI source code for BigDecimal

2000 /* Returns debugging information about the value as a string of comma-separated
2001  * values in angle brackets with a leading #:
2002  *
2003  * BigDecimal.new("1234.5678").inspect ->
2004  * "#<BigDecimal:b7ea1130,'0.12345678E4',8(12)>"
2005  *
2006  * The first part is the address, the second is the value as a string, and
2007  * the final part ss(mm) is the current number of significant digits and the
2008  * maximum number of significant digits, respectively.
2009  */
2010 static VALUE
2011 BigDecimal_inspect(VALUE self)
2012 {
2013     ENTER(5);
2014     Real *vp;
2015     volatile VALUE obj;
2016     size_t nc;
2017     char *psz, *tmp;
2018 
2019     GUARD_OBJ(vp, GetVpValue(self, 1));
2020     nc = VpNumOfChars(vp, "E");
2021     nc += (nc + 9) / 10;
2022 
2023     obj = rb_str_new(0, nc+256);
2024     psz = RSTRING_PTR(obj);
2025     sprintf(psz, "#<BigDecimal:%"PRIxVALUE",'", self);
2026     tmp = psz + strlen(psz);
2027     VpToString(vp, tmp, 10, 0);
2028     tmp += strlen(tmp);
2029     sprintf(tmp, "',%"PRIuSIZE"(%"PRIuSIZE")>", VpPrec(vp)*VpBaseFig(), VpMaxPrec(vp)*VpBaseFig());
2030     rb_str_resize(obj, strlen(psz));
2031     return obj;
2032 }
2033 

所以,你想要的似乎是inspect-string的第二部分,0.1E2在你的情况下,等于10。上面的注释非常清楚,这应该是对象的完整数值。简单的正则表达就足够了。

答案 3 :(得分:1)

BigDecimal#inspect的文档不完整。请考虑以下事项:

require 'bigdecimal`

BigDecimal.new("1.2345").inspect
  #=> "#<BigDecimal:7fb06a110298,'0.12345E1',18(18)>" 
...
BigDecimal.new("1.234567890").inspect
  #=> "#<BigDecimal:7fb06a16ab58,'0.123456789E1',18(27)>" 
BigDecimal.new("1.2345678901").inspect
  #=> "#<BigDecimal:7fb06a14a6a0,'0.1234567890 1E1',27(27)>" 
BigDecimal.new("1.23456789012").inspect
  #=> "#<BigDecimal:7fb06a1393a0,'0.1234567890 12E1',27(27)>" 
BigDecimal.new("1.234567890123").inspect
  #=> "#<BigDecimal:7fb06a123780,'0.1234567890 123E1',27(27)>" 

inspect的源代码可以看出,如果有超过10个有效数字,则每个10个字符由空格分隔(为了便于阅读):

BigDecimal.new("123.456789012345678901234567").inspect
  #=> "#<BigDecimal:7fb06a0ac8b0,'0.1234567890 1234567890 1234567E3',36(36)>" 

我建议检索BigDecimal值的字符串表示形式,如下所示:

str = "#<BigDecimal:7fb06a14a6a0,'0.1234567890 1E1',27(27)>" 
str.delete(' ').split(?')[1]
  #=> "0.12345678901E1"

我们还没完。我们仍然必须将我们提取的字符串转换为数字对象。但是,如果值的绝对值很大,我们就无法使用BigDecimal#to_f

"1.23456789012345678".to_f
  #=> 1.2345678901234567 

最安全的做法是使用方法BigDecimal::new返回BigDecimal对象,该方法有两个参数:

  • 要转换为BigDecimal对象的值,可以是Integer,Float,Rational,BigDecimal或String。如果我们将提供的字符串,#34;空格被忽略,而无法识别的字符终止值&#34; (类似于"123.4cat".to_f #=> 123.4)。

  • 有效位数。如果省略或为零,则从该值确定有效位数。我会省略这个论点。 (例如,BigDecimal.new("0.1234E2").precs #=> [18, 18],其中数组包含当前和最大有效位数。

注意第二个参数是必需的,如果第一个是Float或Rational,否则它是可选的。

我们可以写:

require 'bigdecimal'

def convert(str)
  BigDecimal.new(str.delete(' ').split(?')[1])
end

convert "#<BigDecimal:7facd39d7ee8,'0.1234E4',9(18)>"
  #=> #<BigDecimal:7facd39c7de0,'0.1234E4',9(18)> 
convert "#<BigDecimal:7facd39b7be8,'0.1234E2',18(18)>"
  #=> #<BigDecimal:7facd39ae610,'0.1234E2',18(18)> 
convert "#<BigDecimal:7facd3990638,'0.1234E0',9(18)>"
  #=> #<BigDecimal:7facd3980aa8,'0.1234E0',9(18)> 
convert "#<BigDecimal:7facd3970e28,'0.1234E-2',9(18)>"
  #=> #<BigDecimal:7facd39625d0,'0.1234E-2',9(18)> 
v = convert "#<BigDecimal:7fb06a123780,'0.1234567890 123E1',27(27)>"
  #=> #<BigDecimal:7fb069851d78,'0.1234567890 123E1',27(27)>

在不损失准确性的情况下查看BigDecimal对象是否可以转换为浮点数的简单方法是:

def convert_bd_to_float(bd)
  f = bd.to_f
  (bd==BigDecimal.new(f.to_s)) ? f : nil
end

convert_bd_to_float BigDecimal.new('1234567890123456')
  #=> 1.234567890123456e+15 
convert_bd_to_float BigDecimal.new('12345678901234567')
  #=> nil 

答案 4 :(得分:0)

另一种选择:

 "#<BigDecimal:95915c4,'0.1E2',9(27)>".split(",")[1].tr! "'", ''
=> "0.1E2"