Reputation: 21220
I have a number in actionscript, arrived at via some arbitrary math:
var value:Number = 45 * (1 - (1 /3));
trace(value);//30.00000000004
Now, I would like to take the ceiling of this number, except in cases where the amount it is greater than the next lower integer is smaller than some epsilon. In the above example, I really want to round to 30, but only in the case where I know I'm getting a rounding error:
Math.ceil(value); //I want 30, but get 31
Math.ceil(30.1); //In this case, it's reasonable to get 31
Is there an elegant way to truncate a Number in actionscript? Or easily discard any part of the number that is less than some epsilon?
Upvotes: 0
Views: 1192
Reputation: 1645
The basic way to round a number to a specified number of fractional digits is to multiply the number to 10^DIGITS to shift the decimal point DIGITS digits to the left, perform the rounding, and divide by the same 10^DIGITS to shift the decimal point back to the right.
var value:Number = 45 * (1 - (1 / 3));
trace(value); // 30.000000000000004
trace(Math.ceil(value)); // 31
// Round the number to 13 decimal digits.
const POWER:Number = 1e13;
value = Math.round(value * POWER) / POWER;
trace(value); // 30
// Compute number's ceiling.
value = Math.ceil(value);
trace(value); // 30`
It works for your example, but there's a big gotcha. If you change your value to be 450 * (1 - (1 / 3));
, your original problem will appear again. Now to get rid of it, you would have to round to 12 decimal digits. Basically, the significand of a double-precision format (Number) can hold about 15 significant digits. This means as the value increases by a factor of ten, the decimal points moves to the left and that last "4" digit you want to get rid of becomes closer and closer to the decimal point. So the code becomes more complicated.
var value:Number = 450 * (1 - (1 / 3));
trace(value); // 30.000000000000004
trace(Math.ceil(value)); // 31
var exp:Number = Math.floor(Math.log(Math.abs(value)) * Math.LOG10E);
trace('exp=' + exp); // exp=2
const POWER:Number = Math.pow(10, 14 - exp);
value *= POWER;
trace(value); // 300000000000000.06
value = Math.round(value);
trace(value); // 300000000000000
value /= POWER;
trace(value); // 300
As you can see, it now works regardless of the value's magnitude.
First, I find the number's exponent by taking a base-10 logarithm of the number's absolute value, then rounding it down. If you calculate a = value * Math.pow(10, exp);
, then value could be represented as a * 10^b, where (1 ≤ |a| < 10), known as normalized scientific notation. But that's not what we're doing here. Now that we know how many digits are on the left of the decimal point, we will shift the decimal point right, but not too far, to keep one 0 and this error digit we want to get rid of, on the right side of the decimal point. So, multiply by 10^(14-exp), round, then divide by the same power.
Upvotes: 0
Reputation: 1477
Is this method is of any help to you?
var precision:int = 4;
var isActualCeilingValRequred:Boolean;
var thresholdValForCeiling:int = 100;
private function getCeilingValue(num:Number):Number
{
var tempNum = num * Math.pow(10, precision);
var decimalVal = tempNum % Math.pow(10, precision);
if(decimalVal < thresholdValForCeiling) {
return Math.floor(num);
} else {
return Math.Ceil(num);
}
}
Upvotes: 2
Reputation: 9055
var value:Number = 45 * (1 - (1 /3));
trace(value);//30.00000000004
// Play with arbitraryPrecision until you are satisfied with
// the accuracy of your results
var arbitraryPrecision:int = 3;
var fixed:Number = value.toFixed(arbitraryPrecision);
trace(Math.ceil(fixed));
Upvotes: 1