如何修复浮点不精确的输出?

时间:2008-12-08 17:26:16

标签: actionscript-3 algorithm language-agnostic math floating-point

我正在做一些浮动操作并最终得到以下数字:

-0.5
-0.4
-0.3000000000000000004
-0.2000000000000000004
-0.1000000000000000003
1.10E-16
0.1
0.2
0.30000000000000000004
0.4
0.5

算法如下:

var inc:Number = nextMultiple(min, stepSize);
trace(String(inc));

private function nextMultiple(x:Number, y:Number) {
    return Math.ceil(x/y)*y;
}

我理解浮点数不能总是在一个字节中准确表示。例如1/3。我也知道我的步数为0.1 。如果我有步骤,我怎么能得到一个合适的输出?

奇怪的是,这是我第一次遇到这类问题。 也许我玩不够漂浮。

10 个答案:

答案 0 :(得分:8)

与语言无关的解决方案是将您的数字存储为整数步数,前提是您知道步长而不是浮点数。

非语言无关的解决方案是找出您的语言printf的实现方式。

printf ("float: %.1f\n", number);

答案 1 :(得分:3)

正如您所知,二进制数的有限浮点精度是您的问题。解决这个问题的一种方法是不进行浮点数学运算。将问题转换为整数,然后转换回输出。

答案 2 :(得分:1)

如果您使用的是具有圆形功能的语言,则可以使用该功能。

修改

在回复关于舍入的评论时,这是c#中的一个示例:

float value = 1.0F;

for (int i = 0; i < 20; i++)
{
    value -= 0.1F;
    Console.WriteLine(Math.Round(value, 1).ToString() + " : " + value.ToString());
}

结果是:

0.9:0.9

0.8:0.8

0.7:0.6999999

0.6:0.5999999

(等)

舍入确实解决了精度问题。我并不认为它比做整数数学然后除以10更好,只是它有效。

答案 3 :(得分:1)

使用整数而不是浮点类型,或使用浮点类型,其中“点”是小数点(例如.NET中的System.Decimal)。

答案 4 :(得分:0)

根据您的具体问题,在实际使用某事物的值之前,从-5到5计算并除以10。

答案 5 :(得分:0)

我做了以下,

var digitsNbr:Number = Math.abs(Math.ceil(((Math.log(stepSize) / Math.log(10))) + 1));      
tickTxt.text = String(inc.toPrecision(digitsNbr));

效率不高但我没有太多步骤。

====== 我应该把步骤的nbr作为一个int并逐步加倍...

答案 6 :(得分:0)

如果你没有printf,或者步数不仅仅是10的幂(例如,如果你想要舍入到最近的0.2)那么听起来你想要一个量化器:

q(x,u)= u * floor(x / u + 0.5);

“u”是步长(在你的情况下为0.1),floor()找到不大于其输入的最大整数,“+ 0.5”将舍入到最接近的整数。

所以基本上,你除以步长,舍入到最接近的整数,然后乘以步长。

编辑:哦,没关系,你基本上都是这样做的&amp;它乘以u的步骤是引入舍入误差。

答案 7 :(得分:0)

简单地缩放数字以获得整数,然后进行数学运算并将它们缩放回浮点数进行显示:

//this will round to 3 decimal places
var NUM_SCALE = 1000;

function scaleUpNumber(n) {
    return (Math.floor(n * NUM_SCALE));
}
function scaleDnNumber(n) {
    return (n / NUM_SCALE);
}


var answ = scaleUpNumber(2.1) - scaleUpNumber(3.001);
alert(scaleDnNumber(answ)); // displays: -0.901

更改NUM_SCALE以增加/减少decimap位置

| / | AX

答案 8 :(得分:0)

这有点违反直觉,但我测试了它并且它有效(例如在AS3中):

var inc:Number = nextMultiple(min, stepSize);
trace(String(inc));

private function nextMultiple(x:Number, y:Number) {
    return Math.ceil(x/y)*(y*10)/10;
}

所以我添加的唯一内容是将y乘以10,然后除以10.不是通用解决方案,而是与stepSize一起使用。

[编辑:]这里的逻辑似乎是你乘以一个足够大的数字,以便最后的十进制数字“降低比例”,然后再次除以得到一个四舍五入的数字。也就是说,上面使用 Math.round()的示例更具可读性和更好,因为代码明确说明传入的数字会发生什么。

答案 9 :(得分:0)

如果您的语言支持,最好使用Decimal数据类型。为了解决这个问题,小数被添加到多种语言中。