4

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

-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。如果我有步长,我怎么能得到正确的输出?

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

4

10 回答 10

8

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

与语言无关的解决方案是找出您的语言对printf的实现是什么。

printf ("float: %.1f\n", number);
于 2008-12-08T17:33:01.093 回答
3

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

于 2008-12-08T17:30:33.547 回答
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

(ETC)

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

于 2008-12-08T17:32:34.540 回答
1

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

于 2008-12-08T17:33:14.383 回答
0

对于您的具体问题,在实际使用某事物的值之前,从 -5 数到 5 并除以 10。

于 2008-12-08T17:32:46.617 回答
0

我做了以下,

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

它效率不高,但我没有很多步骤。

====== 我应该将步数的 nbr 作为 int 并按步相乘...

于 2008-12-08T21:32:40.360 回答
0

如果您没有 printf,或者如果步骤不只是 10 的幂(例如,如果您想四舍五入到最接近的 0.2),那么听起来您需要一个量化器:

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

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

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

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

于 2008-12-11T19:52:00.903 回答
0

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

//这将四舍五入到小数点后3位
变量 NUM_SCALE = 1000;

函数 scaleUpNumber(n) {
    返回 (Math.floor(n * NUM_SCALE));
}
函数 scaleDnNumber(n) {
    返回(n / NUM_SCALE);
}


var answ = scaleUpNumber(2.1) - scaleUpNumber(3.001);
警报(scaleDnNumber(answ));// 显示:-0.901

更改 NUM_SCALE 以增加/减少十进制地图位置

|/|斧头

于 2008-12-21T11:46:58.753 回答
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;
}

所以我唯一添加的是乘以y10,然后除以 10。这不是通用解决方案,但适用于您的 stepSize。

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

于 2008-12-31T15:54:14.660 回答
0

如果您的语言支持,最好的选择是使用 Decimal 数据类型。小数被添加到许多语言中以解决这个确切的问题。

于 2008-12-31T15:57:51.363 回答