Ruby 鲁比:为什么1.025.四舍五入(2)到1.02?

Ruby 鲁比:为什么1.025.四舍五入(2)到1.02?,ruby,floating-point,decimal,rounding,Ruby,Floating Point,Decimal,Rounding,据我所知,ruby中的-功能向上舍入小数,其中最后一个有效数字是5 例如1.5.四舍五入(0)#=>2(确定) 但是为什么1.025.round(2)#=>1.02,而不是我所期望的1.03 irb(main):037:0> 1.025.round(2) => 1.02 我能做些什么来解决这个问题?这与最后一个数字是5无关,而是与十进制值到双精度浮点值的转换有关 基本上,十进制数必须以有限的二进制格式表示,该格式只能近似于某些十进制值,从而导致精度损失。正如您所看到的,这可能会导

据我所知,ruby中的-功能向上舍入小数,其中最后一个有效数字是
5

例如
1.5.四舍五入(0)#=>2
(确定)

但是为什么
1.025.round(2)#=>1.02
,而不是我所期望的
1.03

irb(main):037:0> 1.025.round(2)
=> 1.02

我能做些什么来解决这个问题?

这与最后一个数字是5无关,而是与十进制值到双精度浮点值的转换有关

基本上,十进制数必须以有限的二进制格式表示,该格式只能近似于某些十进制值,从而导致精度损失。正如您所看到的,这可能会导致一些奇怪的行为

最好通过向你展示
Marshal.dump(1.025)
转储浮点值并显示更接近实际值的值:1.02499999999999<代码>1.025。to_r将为您提供表示二进制值的分数。您可以使用任意精确的十进制库BigDecimal来转换:

ruby-1.9.2-p180 :060 > (BigDecimal.new("2308094809027379.0") / BigDecimal.new("2251799813685248.0")).to_s('F')
=> "1.024999999999999911182158029987476766"
当某些小数转换为这种“近似”二进制数格式时,它们将以不同的方式表示,可能更精确。因此,您可能已经注意到,
1.085.round(2)
产生了您所期望的1.09

浮点数学缺乏精确性意味着在货币计算中使用浮点值,甚至作为货币值的临时容器,永远都不合适。任何涉及金钱的事情都应始终使用任意精度的数据类型

作为一家非常大的金融公司的前开发人员,我经常感到震惊的是,人们很少注意到这一建议,而且在金融软件中使用浮点数或双倍点数是多么普遍。与我交谈过的这个行业的大多数程序员都不知道浮动和双精度不应该存储货币值。所以,不要觉得自己太落后了;-)

tl;博士
使用BigDecimal:
BigDecimal.new(“1.025”).round(2)
=>“1.03”

我认为这是由于浮点数的性质。它们不是数字的精确表示:

printf("%f", 1.025)     # rounded to 6 decimal places
=> 1.025000

printf("%.16f", 1.025)  # rounded to 16 decimal places
=> 1.0249999999999999
因此,当你输入“1.025”时,它在计算机中表示为一个比你真正想要的数值小一点点的数字。大多数时候,这不是问题,但它会偶尔吐出一些奇怪的东西

需要明确的是:这不是Ruby的问题,而是所有语言中的浮点数的问题。如果它给您带来了麻烦,请查看BigDecimal。

使用它,您可以查看Float#round的底层代码

因此,在撬式:

show-method Float#round
下面显示的是底层C代码:

From:Ruby内核中的numeric.c(c方法): 行数:36

static VALUE
flo_round(int argc, VALUE *argv, VALUE num)
{
    VALUE nd;
    double number, f;
    int ndigits = 0, i;
    long val;

    if (argc > 0 && rb_scan_args(argc, argv, "01", &nd) == 1) {
    ndigits = NUM2INT(nd);
    }
    number  = RFLOAT_VALUE(num);
    f = 1.0;
    i = abs(ndigits);
    while  (--i >= 0)
    f = f*10.0;

    if (isinf(f)) {
    if (ndigits < 0) number = 0;
    }
    else {
    if (ndigits < 0) number /= f;
    else number *= f;
    number = round(number);
    if (ndigits < 0) number *= f;
    else number /= f;
    }

    if (ndigits > 0) return DBL2NUM(number);

    if (!FIXABLE(number)) {
    return rb_dbl2big(number);
    }
    val = (long)number;
    return LONG2FIX(val);
}
静态值
flo_round(int argc,VALUE*argv,VALUE num)
{
nd值;
双数,f;
int ndigits=0,i;
长瓦尔;
如果(argc>0&&rb\u scan\u args(argc,argv,“01”,&nd)==1){
ndigits=NUM2INT(nd);
}
数字=RFLOAT_值(num);
f=1.0;
i=绝对值(ndigits);
而(--i>=0)
f=f*10.0;
if(isinf(f)){
如果(ndigits<0)数=0;
}
否则{
如果(ndigits<0)数/=f;
else数*=f;
数字=圆形(数字);
如果(ndigits<0)数*=f;
else数/=f;
}
如果(ndigits>0)返回db2num(number);
如果(!可固定(编号)){
返回rb_dbl2big(编号);
}
val=(长)个数;
返回LONG2FIX(val);
}
哪个节目用的是C轮函数。这是遵守的


除非您有一个非常奇怪的边缘情况,否则我建议您继续使用这种舍入方式。

可能是这样。无论您做什么,请提交一份错误报告!哇,太好笑了,安德鲁。。。必须经常发生才能准备好一个固定的回应。在2011年的RubyKaigi大会上,我听说过一句话“浮动就是遗产”。这是Kenta Murata演讲的名字,但我还没有看过它的幻灯片。
printf
比我的
Marhsal.dump
示例“甚至不作为货币价值的临时容器”更能说明精度损失。救了我!