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. Если у меня есть шаги, как я могу получить правильный вывод?

Странно то, что в первый раз я столкнулся с этой проблемой. Возможно, я не играю с поплавком.

+0

Ничто не может точно представлять 1/3. т. е. 3 * (1/3) == 0.99999 ... – 2008-12-08 18:49:19

+0

И в базе 2 невозможно представить 0,1 отлично. – duffymo 2008-12-26 13:54:47

+0

@BradGilbert Base 3 может. – 2012-07-19 06:27:04

ответ

8

Языковое агностическое решение состоит в том, чтобы хранить ваши числа как целое число шагов, учитывая, что вы знаете свой размер шага, а не как плавающие.

Негалогическое агностическое решение будет состоять в том, чтобы выяснить, что такое реализация вашего языка printf.

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

Ограниченная точность двоичных чисел с плавающей запятой является вашей проблемой, как вы признаете. Один из способов - не делать математику с плавающей запятой. Переведите проблему на целые числа, а затем переведите обратно для вывода.

+0

Я бы указал, рациональные числа – nlucaroni 2008-12-08 18:10:40

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,69999 99

0,6: 0,5999999

(и т.д.)

Округление делает решить проблему точности. Я не утверждаю, что это лучше, чем выполнение целочисленной математики, а затем деление на 10, только то, что она работает.

0

С вашей конкретной проблемой, считайте от -5 до 5 и делите на 10, прежде чем фактически использовать значение для чего-то.

1

либо использовать целые числа вместо типа с плавающей точкой, или использовать тип с плавающей точкой, где «точка» является десятичной точкой (например, System.Decimal в .NET).

0

я сделал следующее,

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

Его не эффективен, но я не имею много шагов.

====== я должен просто получить НБР шагов в качестве междунар и умножить на шаг ...

0

Если вы не имеете Printf, или если шаги не только полномочия 10 (например, если вы хотите округлить до ближайшего 0,2), то похоже, что вы хотите квантователь:

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

«u» - это размер шага (0,1 в вашем случае), floor() находит наибольшее целое число, не превышающее его вход, а «+ 0,5» округляется до ближайшего целого.

Итак, в основном вы делите на размер шага, округлите до ближайшего целого и умножьте на размер шага.

Редактирование: о, неважно, вы в основном делаете это так или иначе & шаг, на котором он умножается на u, представляет ошибку округления.

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 помещает

|/| топор

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. Не универсальное решение, но работает с вашим шагом.

[править] Логика здесь, кажется, состоит в том, что вы умножаетесь на достаточно большое число, чтобы последние десятичные цифры «выпадали из шкалы», а затем разделили снова, чтобы получить округленное число. Тем не менее, приведенный выше пример, который использует Math.round(), более читабельна и лучше в том смысле, что код явно говорит о том, что произойдет с пропущенными числами.

0

. Лучше всего использовать данные десятичных разрядов введите, если ваш язык поддерживает его. Десятичные числа были добавлены на несколько языков для борьбы с этой точной проблемой.

Смежные вопросы