Calculating cubic root for negative number - javascript

So, to be short,
3√(-8) = (-8)1/3
console.log(Math.pow(-8,1/3));
//Should be -2
But when I test it out, it outputs
NaN
Why? Is it a bug or it is expected to be like this in the first place? I am using JavaScript to draw graphs, but this messes up the graph.

You can use this snippet to calculate it. It also works for other powers, e.g. 1/4, 1/5, etc.
function nthroot(x, n) {
try {
var negate = n % 2 == 1 && x < 0;
if(negate)
x = -x;
var possible = Math.pow(x, 1 / n);
n = Math.pow(possible, n);
if(Math.abs(x - n) < 1 && (x > 0 == n > 0))
return negate ? -possible : possible;
} catch(e){}
}
nthroot(-8, 3);
Source: http://gotochriswest.com/blog/2011/05/06/cube-root-an-beyond/
A faster approach for just calculating the cubic root:
Math.cbrt = function(x) {
var sign = x === 0 ? 0 : x > 0 ? 1 : -1;
return sign * Math.pow(Math.abs(x), 1 / 3);
}
Math.cbrt(-8);
Update
To find an integer based cubic root, you can use the following function, inspired by this answer:
// positive-only cubic root approximation
function cbrt(n)
{
var a = n; // note: this is a non optimized assumption
while (a * a * a > n) {
a = Math.floor((2 * a + (n / (a * a))) / 3);
}
return a;
}
It starts with an assumption that converges to the closest integer a for which a^3 <= n. This function can be adjusted in the same way to support a negative base.

There's no bug; you are raising a negative number to a fractional power; hence, the NaN.
The top hit on google for this is from Dr Math the explanation is pretty good. It says for for real numbers (not complex numbers anyway), a negative number raised to a fractional power may not be a real number. The simplest example is probably
-4 ^ (1/2)
which is essentially computing the square root of -4. Even though the cubic root of -8 does have real solutions, I think that most software libraries find it more efficient not to do all the complex arithmetic and return NaN only when the imaginary part is nonzero and give you the nice real answer otherwise.
EDIT
Just to make absolutely clear that NaN is the intended result, see the official ECMAScript 5.1 Specification, Section 15.8.2.13. It says:
If x<0 and x is finite and y is finite and y is not an integer, the result is NaN.
Again, even though SOME instances of raising negative numbers to fractional powers have exactly one real root, many languages just do the NaN thing for all cases of negative numbers to fractional roots.
Please do not think JavaScript is the only such language. C++ does the same thing:
If x is finite negative and y is finite but not an integer value, it causes a domain error.

Two key problems:
Mathematically, there are multiple cubic roots of a negative number: -2, but also 2 complex roots (see cube roots of unity).
Javascript's Math object (and most other standard math libraries) will not do fractional powers of negative numbers. It converts the fractional power to a float before the function receives it, so you are asking the function to compute a floating point power of a negative number, which may or may not have a real solution. So it does the pragmatic thing and refuses to attempt to calculate such a value.
If you want to get the correct answer, you'll need to decide how mathematically correct you want to be, and write those rules into a non-standard implementation of pow.
All library functions are limited to avoid excessive calculation times and unnecessary complexity.

I like the other answers, but how about overriding Math.pow so it would be able to work with all nth roots of negative numbers:
//keep the original method for proxying
Math.pow_ = Math.pow;
//redefine the method
Math.pow = function(_base, _exponent) {
if (_base < 0) {
if (Math.abs(_exponent) < 1) {
//we're calculating nth root of _base, where n === 1/_exponent
if (1 / _exponent % 2 === 0) {
//nth root of a negative number is imaginary when n is even, we could return
//a string like "123i" but this would completely mess up further computation
return NaN;
}/*else if (1 / _exponent % 2 !== 0)*/
//nth root of a negative number when n is odd
return -Math.pow_(Math.abs(_base), _exponent);
}
}/*else if (_base >=0)*/
//run the original method, nothing will go wrong
return Math.pow_(_base, _exponent);
};
Fiddled with some test cases, give me a shout if you spot a bug!

So I see a bunch of methods that revolve around Math.pow(...) which is cool, but based on the wording of the bounty I'm proposing a slightly different approach.
There are several computational approximations for solving roots, some taking quicker steps than others. Ultimately the stopping point comes down to the degree of precision desired(it's really up to you/the problem being solved).
I'm not going to explain the math in fine detail, but the following are implementations of cubed root approximations that passed the target test(bounty test - also added negative range, because of the question title). Each iteration in the loop (see the while(Math.abs(xi-xi0)>precision) loops in each method) gets a step closer to the desired precision. Once precision is achieved a format is applied to the number so it's as precise as the calculation derived from the iteration.
var precision = 0.0000000000001;
function test_cuberoot_fn(fn) {
var tested = 0,
failed = 0;
for (var i = -100; i < 100; i++) {
var root = fn(i*i*i);
if (i !== root) {
console.log(i, root);
failed++;
}
tested++;
}
if (failed) {
console.log("failed %d / %d", failed, tested);
}else{
console.log("Passed test");
}
}
test_cuberoot_fn(newtonMethod);
test_cuberoot_fn(halleysMethod);
Newton's approximation Implementation
function newtonMethod(cube){
if(cube == 0){//only John Skeet and Chuck Norris
return 0; //can divide by zero, we'll have
} //to settle for check and return
var xi = 1;
var xi0 = -1;
while(Math.abs(xi-xi0)>precision){//precision = 0.0000000000001
xi0=xi;
xi = (1/3)*((cube/(xi*xi))+2*xi);
}
return Number(xi.toPrecision(12));
}
Halley's approximation Implementation
note Halley's approximation takes quicker steps to solving the cube, so it's computationally faster than newton's approximation.
function halleysMethod(cube){
if(cube == 0){//only John Skeet and Chuck Norris
return 0; //can divide by zero, we'll have
} //to settle for check and return
var xi = 1;
var xi0 = -1;
while(Math.abs(xi-xi0)>precision){//precision = 0.0000000000001
xi0=xi;
xi = xi*((xi*xi*xi + 2*cube)/(2*xi*xi*xi+cube));
}
return Number(xi.toPrecision(12));
}

It's Working in Chrome Console
function cubeRoot(number) {
var num = number;
var temp = 1;
var inverse = 1 / 3;
if (num < 0) {
num = -num;
temp = -1;
}
var res = Math.pow(num, inverse);
var acc = res - Math.floor(res);
if (acc <= 0.00001)
res = Math.floor(res);
else if (acc >= 0.99999)
res = Math.ceil(res);
return (temp * res);
}
cubeRoot(-64) // -4
cubeRoot(64) // 4

As a heads up, in ES6 there is now a Math.cbrt function.
In my testing in Google chrome it appears to work almost twice as fast as Math.pow. Interestingly I had to add up the results otherwise chrome did a better job of optimizing away the pow function.
//do a performance test on the cube root function from es6
var start=0, end=0, k=0;
start = performance.now();
k=0;
for (var i=0.0; i<10000000.0; i+=1.0)
{
var j = Math.cbrt(i);
//k+=j;
}
end = performance.now();
console.log("cbrt took:" + (end-start),k);
k=0;
start = performance.now();
for (var i=0.0; i<10000000.0; i+=1.0)
{
var j = Math.pow(i,0.33333333);
//k+=j;
}
end = performance.now();
console.log("pow took:" + (end-start),k);
k=0;
start = performance.now();
for (var i=0.0; i<10000000.0; i+=1.0)
{
var j = Math.cbrt(i);
k+=j;
}
end = performance.now();
console.log("cbrt took:" + (end-start),k);
k=0;
start = performance.now();
for (var i=0.0; i<10000000.0; i+=1.0)
{
var j = Math.pow(i,0.33333333);
k+=j;
}
end = performance.now();
console.log("pow took:" + (end-start),k);
Result:
cbrt took:468.28200000163633 0
pow took:77.21999999921536 0
cbrt took:546.8039999977918 1615825909.5248165
pow took:869.1149999940535 1615825826.7510242

//aren't cube roots of negative numbers the same as positive, except for the sign?
Math.cubeRoot= function(n, r){
var sign= (n<0)? -1: 1;
return sign*Math.pow(Math.abs(n), 1/3);
}
Math.cubeRoot(-8)
/* returned value: (Number)
-2
*/

Just want to highlight that in ES6 there is a native cubic root function. So you can just do this (check the support here)
Math.cbrt(-8) will return you -2

this works with negative number and negative exponent:
function nthRoot(x = 0, r = 1) {
if (x < 0) {
if (r % 2 === 1) return -nthRoot(-x, r)
if (r % 2 === -1) return -1 / nthRoot(-x, -r)
}
return x ** (1 / r)
}
examples:
nthRoot( 16, 2) 4
nthRoot( 16, -2) 0.25
nthRoot(-16, 2) NaN
nthRoot(-16, -2) NaN
nthRoot( 27, 3) 3
nthRoot( 27, -3) 0.3333333333333333
nthRoot(-27, 3) -3
nthRoot(-27, -3) -0.3333333333333333

Related

Trying to optimize my code to either remove nested loop or make it more efficient

A friend of mine takes a sequence of numbers from 1 to n (where n > 0)
Within that sequence, he chooses two numbers, a and b
He says that the product of a and b should be equal to the sum of all numbers in the sequence, excluding a and b
Given a number n, could you tell me the numbers he excluded from the sequence?
Have found the solution to this Kata from Code Wars but it times out (After 12 seconds) in the editor when I run it; any ideas as too how I should further optimize the nested for loop and or remove it?
function removeNb(n) {
var nArray = [];
var sum = 0;
var answersArray = [];
for (let i = 1; i <= n; i++) {
nArray.push(n - (n - i));
sum += i;
}
var length = nArray.length;
for (let i = Math.round(n / 2); i < length; i++) {
for (let y = Math.round(n / 2); y < length; y++) {
if (i != y) {
if (i * y === sum - i - y) {
answersArray.push([i, y]);
break;
}
}
}
}
return answersArray;
}
console.log(removeNb(102));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I think there is no reason for calculating the sum after you fill the array, you can do that while filling it.
function removeNb(n) {
let nArray = [];
let sum = 0;
for(let i = 1; i <= n; i++) {
nArray.push(i);
sum += i;
}
}
And since there could be only two numbers a and b as the inputs for the formula a * b = sum - a - b, there could be only one possible value for each of them. So, there's no need to continue the loop when you find them.
if(i*y === sum - i - y) {
answersArray.push([i,y]);
break;
}
I recommend looking at the problem in another way.
You are trying to find two numbers a and b using this formula a * b = sum - a - b.
Why not reduce the formula like this:
a * b + a = sum - b
a ( b + 1 ) = sum - b
a = (sum - b) / ( b + 1 )
Then you only need one for loop that produces the value of b, check if (sum - b) is divisible by ( b + 1 ) and if the division produces a number that is less than n.
for(let i = 1; i <= n; i++) {
let eq1 = sum - i;
let eq2 = i + 1;
if (eq1 % eq2 === 0) {
let a = eq1 / eq2;
if (a < n && a != i) {
return [[a, b], [b, a]];
}
}
}
You can solve this in linear time with two pointers method (page 77 in the book).
In order to gain intuition towards a solution, let's start thinking about this part of your code:
for(let i = Math.round(n/2); i < length; i++) {
for(let y = Math.round(n/2); y < length; y++) {
...
You already figured out this is the part of your code that is slow. You are trying every combination of i and y, but what if you didn't have to try every single combination?
Let's take a small example to illustrate why you don't have to try every combination.
Suppose n == 10 so we have 1 2 3 4 5 6 7 8 9 10 where sum = 55.
Suppose the first combination we tried was 1*10.
Does it make sense to try 1*9 next? Of course not, since we know that 1*10 < 55-10-1 we know we have to increase our product, not decrease it.
So let's try 2*10. Well, 20 < 55-10-2 so we still have to increase.
3*10==30 < 55-3-10==42
4*10==40 < 55-4-10==41
But then 5*10==50 > 55-5-10==40. Now we know we have to decrease our product. We could either decrease 5 or we could decrease 10, but we already know that there is no solution if we decrease 5 (since we tried that in the previous step). So the only choice is to decrease 10.
5*9==45 > 55-5-9==41. Same thing again: we have to decrease 9.
5*8==40 < 55-5-8==42. And now we have to increase again...
You can think about the above example as having 2 pointers which are initialized to the beginning and end of the sequence. At every step we either
move the left pointer towards right
or move the right pointer towards left
In the beginning the difference between pointers is n-1. At every step the difference between pointers decreases by one. We can stop when the pointers cross each other (and say that no solution can be obtained if one was not found so far). So clearly we can not do more than n computations before arriving at a solution. This is what it means to say that the solution is linear with respect to n; no matter how large n grows, we never do more than n computations. Contrast this to your original solution, where we actually end up doing n^2 computations as n grows large.
Hassan is correct, here is a full solution:
function removeNb (n) {
var a = 1;
var d = 1;
// Calculate the sum of the numbers 1-n without anything removed
var S = 0.5 * n * (2*a + (d *(n-1)));
// For each possible value of b, calculate a if it exists.
var results = [];
for (let numB = a; numB <= n; numB++) {
let eq1 = S - numB;
let eq2 = numB + 1;
if (eq1 % eq2 === 0) {
let numA = eq1 / eq2;
if (numA < n && numA != numB) {
results.push([numA, numB]);
results.push([numB, numA]);
}
}
}
return results;
}
In case it's of interest, CY Aries pointed this out:
ab + a + b = n(n + 1)/2
add 1 to both sides
ab + a + b + 1 = (n^2 + n + 2) / 2
(a + 1)(b + 1) = (n^2 + n + 2) / 2
so we're looking for factors of (n^2 + n + 2) / 2 and have some indication about the least size of the factor. This doesn't necessarily imply a great improvement in complexity for the actual search but still it's kind of cool.
This is part comment, part answer.
In engineering terms, the original function posted is using "brute force" to solve the problem, iterating every (or more than needed) possible combinations. The number of iterations is n is large - if you did all possible it would be
n * (n-1) = bazillio n
Less is More
So lets look at things that can be optimized, first some minor things, I'm a little confused about the first for loop and nArray:
// OP's code
for(let i = 1; i <= n; i++) {
nArray.push(n - (n - i));
sum += i;
}
??? You don't really use nArray for anything? Length is just n .. am I so sleep deprived I'm missing something? And while you can sum a consecutive sequence of integers 1-n by using a for loop, there is a direct and easy way that avoids a loop:
sum = ( n + 1 ) * n * 0.5 ;
THE LOOPS
// OP's loops, not optimized
for(let i = Math.round(n/2); i < length; i++) {
for(let y = Math.round(n/2); y < length; y++) {
if(i != y) {
if(i*y === sum - i - y) {
Optimization Considerations:
I see you're on the right track in a way, cutting the starting i, y values in half since the factors . But you're iterating both of them in the same direction : UP. And also, the lower numbers look like they can go a little below half of n (perhaps not because the sequence start at 1, I haven't confirmed that, but it seems the case).
Plus we want to avoid division every time we start an instantiation of the loop (i.e set the variable once, and also we're going to change it). And finally, with the IF statements, i and y will never be equal to each other the way we're going to create the loops, so that's a conditional that can vanish.
But the more important thing is the direction of transversing the loops. The smaller factor low is probably going to be close to the lowest loop value (about half of n) and the larger factor hi is probably going to be near the value of n. If we has some solid math theory that said something like "hi will never be less than 0.75n" then we could make a couple mods to take advantage of that knowledge.
The way the loops are show below, they break and iterate before the hi and low loops meet.
Moreover, it doesn't matter which loop picks the lower or higher number, so we can use this to shorten the inner loop as number pairs are tested, making the loop smaller each time. We don't want to waste time checking the same pair of numbers more than once! The lower factor's loop will start a little below half of n and go up, and the higher factor's loop will start at n and go down.
// Code Fragment, more optimized:
let nHi = n;
let low = Math.trunc( n * 0.49 );
let sum = ( n + 1 ) * n * 0.5 ;
// While Loop for the outside (incrementing) loop
while( low < nHi ) {
// FOR loop for the inside decrementing loop
for(let hi = nHi; hi > low; hi--) {
// If we're higher than the sum, we exit, decrement.
if( hi * low + hi + low > sum ) {
continue;
}
// If we're equal, then we're DONE and we write to array.
else if( hi * low + hi + low === sum) {
answersArray.push([hi, low]);
low = nHi; // Note this is if we want to end once finding one pair
break; // If you want to find ALL pairs for large numbers then replace these low = nHi; with low++;
}
// And if not, we increment the low counter and restart the hi loop from the top.
else {
low++;
break;
}
} // close for
} // close while
Tutorial:
So we set the few variables. Note that low is set slightly less than half of n, as larger numbers look like they could be a few points less. Also, we don't round, we truncate, which is essentially "always rounding down", and is slightly better for performance, (though it dosenit matter in this instance with just the single assignment).
The while loop starts at the lowest value and increments, potentially all the way up to n-1. The hi FOR loop starts at n (copied to nHi), and then decrements until the factor are found OR it intercepts at low + 1.
The conditionals:
First IF: If we're higher than the sum, we exit, decrement, and continue at a lower value for the hi factor.
ELSE IF: If we are EQUAL, then we're done, and break for lunch. We set low = nHi so that when we break out of the FOR loop, we will also exit the WHILE loop.
ELSE: If we get here it's because we're less than the sum, so we need to increment the while loop and reset the hi FOR loop to start again from n (nHi).

Converting a base 10 number to other bases 2 without built in Javascript functions

I am new to coding and javascript and was asked, for an assignment, to convert base 10 numbers to a binary base without using specific Javascript built in methods (like alert(a.toString(16))), and I am only allowed to use loops,arrays and functions. This is what i have so far:
var number = prompt("Enter an unsigned base 10 number");
if (number>=0) {
var base = prompt("Enter b for binary, o for octal, or h for hexadecimal");
if (base=="h"||base=="H") {
;
}
So as you can see, I don't have much to go on. I was curious as to what equation or formula I would use to convert the base 10 number, as well as how i'm supposed to show A=10, B=11, C=12 and so forth for a hexadecimal base. Any help would be greatly appreciated!
edit: This is a rather complicated way to do it,
as Alnitak showed me (see discussion below).
It is more a scibble, or the long way by foot.
Short explanation:
If we want to get the binary of the decimal number 10,
we have to try 2^n so that 2^n is still smaller than 10.
For example 2^3 = 8 (that is OK). But 2^4 = 16 (thats too big).
So we have 2^3 and store a 1 for that in an array at index 3.
Now we have to get the rest of 10-2^3, which is 2, and have to
make the same calculation again until we get a difference of zero.
At last we have to reverse the array because its the other way arround.
var a = prompt("Enter an unsigned base 10 number");
var arr = [];
var i = 0;
function decToBin(x) {
y = Math.pow(2, i);
if (y < x) {
arr[i] = 0;
i++;
decToBin(x);
} else if (y > x) {
i--;
newX = (x - Math.pow(2, i));
arr[i] = 1;
i = 0;
decToBin(newX)
} else if (y == x) {
arr[i] = 1;
result = arr.reverse().join();
}
return result;
}
var b = decToBin(a); // var b holds the result
document.write(b);

Max length of collatz sequence - optimisation

I'm trying to solve this MaxCollatzLength kata but I'm struggling to optimise it to run fast enough for really large numbers.
In this kata we will take a look at the length of collatz sequences.
And how they evolve. Write a function that take a positive integer n
and return the number between 1 and n that has the maximum Collatz
sequence length and the maximum length. The output has to take the
form of an array [number, maxLength] For exemple the Collatz sequence
of 4 is [4,2,1], 3 is [3,10,5,16,8,4,2,1], 2 is [2,1], 1 is [ 1 ], so
MaxCollatzLength(4) should return [3,8]. If n is not a positive
integer, the function have to return [].
As you can see, numbers in Collatz sequences may exceed n. The last
tests use random big numbers so you may consider some optimisation in
your code:
You may get very unlucky and get only hard numbers: try submitting 2-3
times if it times out; if it still does, probably you need to optimize
your code more;
Optimisation 1: when calculating the length of a
sequence, if n is odd, what 3n+1 will be ?
Optimisation 2: when looping through 1 to n, take i such that i < n/2, what
will be the length of the sequence for 2i ?
A recursive solution quickly blows the stack, so I'm using a while loop. I think I've understood and applied the first optimisation. I also spotted that for n that is a power of 2, the max length will be (log2 of n) + 1 (that only shaves off a very small amount of time for an arbirtarily large number). Finally I have memoised the collatz lengths computed so far to avoid recalculations.
I don't understand what is meant by the second optimisation, however. I've tried to notice a pattern with a few random samples and loops and I've plotted the max collatz lengths for n < 50000. I noticed it seems to roughly follow a curve but I don't know how to proceed - is this a red herring?
I'm ideally looking for a hints in the right direction so I can work towards the solution myself.
function collatz(n) {
let result = [];
while (n !== 1) {
result.push(n);
if (n % 2 === 0) n /= 2;
else {
n = n * 3 + 1;
result.push(n);
n = n / 2;
}
}
result.push(1);
return result;
}
function collatzLength(n) {
if (n <= 1) return 1;
if (!collatzLength.precomputed.hasOwnProperty(n)) {
// powers of 2 are logarithm2 + 1 long
if ((n & (n - 1)) === 0) {
collatzLength.precomputed[n] = Math.log2(n) + 1;
} else {
collatzLength.precomputed[n] = collatz(n).length;
}
}
return collatzLength.precomputed[n];
}
collatzLength.precomputed = {};
function MaxCollatzLength(n) {
if (typeof n !== 'number' || n === 0) return [];
let maxLen = 0;
let numeralWithMaxLen = Infinity;
while (n !== 0) {
let lengthOfN = collatzLength(n);
if (lengthOfN > maxLen) {
maxLen = lengthOfN;
numeralWithMaxLen = n;
}
n--;
}
return [numeralWithMaxLen, maxLen];
}
Memoization is the key to good performance here. You memoize the end results of the function that calculates the Collatz sequence. This will help you on repeated calls to maxCollatzLength, but not when you determine the length of the sequence for the first time.
Also, as #j_random_hacker mentioned, there is no need to actually create the sequence as list; it is enough to store its length. An integer result is light-weight enough to be memoized easily.
You can make use of precalculated results already when you determine the length of a Collatz sequence. Instead of following the sequence all the way down, follow it until you hit a number for which the length is known.
The other optimizations you make are micro-optimizations. I'm not sure that calculating the log for powers of two really buys you anything. It rather burdens you with an extra test.
The memoized implementation below even forgoes the check for 1 by putting 1 in the dictionary of precalculated values initially.
var precomp = {1: 1};
function collatz(n) {
var orig = n;
var len = 0;
while (!(n in precomp)) {
n = (n % 2) ? 3*n + 1 : n / 2;
len++;
}
return (precomp[orig] = len + precomp[n]);
}
function maxCollatz(n) {
var res = [1, 1];
for (var k = 2; k <= n; k++) {
var c = collatz(k);
if (c > res[1]) {
res[0] = k;
res[1] = c;
}
}
return res;
}
I haven't used node.js, but the JavaScript in my Firefox. It gives reasonable performance. I first had collatz as a recursive function, which made the implementation only slightly faster than yours.
The second optimization mentioned in the question means that if you know C(n), you also know that C(2*n) == C(n) + 1. You could use that knowledge to precalculate the values for all even n in a bottom-up approach.
It would be nice if the lengths of the Collatz sequences could be calculated from the bottom up, a bit like the sieve of Erathostenes. You have to know where you come from instead of where you go to, but it is hard to know ehen to stop, because for finding the longest sequence for n < N, you will have to calculate many sequences out of bound with n > N. As is, the memoization is a good way to avoid repetition in an otherwise straightforwad iterative approach.
In this task you are required to write a Python function,
maxLength, that returns two integers:
• First returned value: for each integer k, 1 ≤ k ≤ m, the
length of Collatz sequence for each k is computed and the
largest of these numbers is returned.
• Second returned value is the integer k, 1 ≤ k ≤ m, whose
Collatz sequence has the largest length. In case there are
several such numbers, return the first one (the smallest).
For example, maxLength(10) returns numbers
20 and 9
Which means that among the numbers 1, 2, 3,…, 10, nine has the
longest Collatz sequence, and its length is equal to 20.
In your program you may define other (auxiliary) functions with
arbitrary names, however, the solution function of this task
should be named maxLength(m).

How do I get the decimal places of a floating point number in Javascript?

What I would like to have is the almost opposite of Number.prototype.toPrecision(), meaning that when i have number, how many decimals does it have? E.g.
(12.3456).getDecimals() // 4
For anyone wondering how to do this faster (without converting to string), here's a solution:
function precision(a) {
var e = 1;
while (Math.round(a * e) / e !== a) e *= 10;
return Math.log(e) / Math.LN10;
}
Edit: a more complete solution with edge cases covered:
function precision(a) {
if (!isFinite(a)) return 0;
var e = 1, p = 0;
while (Math.round(a * e) / e !== a) { e *= 10; p++; }
return p;
}
One possible solution (depends on the application):
var precision = (12.3456 + "").split(".")[1].length;
If by "precision" you mean "decimal places", then that's impossible because floats are binary. They don't have decimal places, and most values that have a small number of decimal places have recurring digits in binary, and when they're translated back to decimal that doesn't necessarily yield the original decimal number.
Any code that works with the "decimal places" of a float is liable to produce unexpected results on some numbers.
There is no native function to determine the number of decimals. What you can do is convert the number to string and then count the offset off the decimal delimiter .:
Number.prototype.getPrecision = function() {
var s = this + "",
d = s.indexOf('.') + 1;
return !d ? 0 : s.length - d;
};
(123).getPrecision() === 0;
(123.0).getPrecision() === 0;
(123.12345).getPrecision() === 5;
(1e3).getPrecision() === 0;
(1e-3).getPrecision() === 3;
But it's in the nature of floats to fool you. 1 may just as well be represented by 0.00000000989 or something. I'm not sure how well the above actually performs in real life applications.
Basing on #blackpla9ue comment and considering numbers exponential format:
function getPrecision (num) {
var numAsStr = num.toFixed(10); //number can be presented in exponential format, avoid it
numAsStr = numAsStr.replace(/0+$/g, '');
var precision = String(numAsStr).replace('.', '').length - num.toFixed().length;
return precision;
}
getPrecision(12.3456); //4
getPrecision(120.30003300000); //6, trailing zeros are truncated
getPrecision(15); //0
getPrecision(120.000)) //0
getPrecision(0.0000005); //7
getPrecision(-0.01)) //2
Try the following
function countDecimalPlaces(number) {
var str = "" + number;
var index = str.indexOf('.');
if (index >= 0) {
return str.length - index - 1;
} else {
return 0;
}
}
Based on #boolean_Type's method of handling exponents, but avoiding the regex:
function getPrecision (value) {
if (!isFinite(value)) { return 0; }
const [int, float = ''] = Number(value).toFixed(12).split('.');
let precision = float.length;
while (float[precision - 1] === '0' && precision >= 0) precision--;
return precision;
}
Here are a couple of examples, one that uses a library (BigNumber.js), and another that doesn't use a library. Assume you want to check that a given input number (inputNumber) has an amount of decimal places that is less than or equal to a maximum amount of decimal places (tokenDecimals).
With BigNumber.js
import BigNumber from 'bignumber.js'; // ES6
// const BigNumber = require('bignumber.js').default; // CommonJS
const tokenDecimals = 18;
const inputNumber = 0.000000000000000001;
// Convert to BigNumber
const inputNumberBn = new BigNumber(inputNumber);
// BigNumber.js API Docs: http://mikemcl.github.io/bignumber.js/#dp
console.log(`Invalid?: ${inputNumberBn.dp() > tokenDecimals}`);
Without BigNumber.js
function getPrecision(numberAsString) {
var n = numberAsString.toString().split('.');
return n.length > 1
? n[1].length
: 0;
}
const tokenDecimals = 18;
const inputNumber = 0.000000000000000001;
// Conversion of number to string returns scientific conversion
// So obtain the decimal places from the scientific notation value
const inputNumberDecimalPlaces = inputNumber.toString().split('-')[1];
// Use `toFixed` to convert the number to a string without it being
// in scientific notation and with the correct number decimal places
const inputNumberAsString = inputNumber.toFixed(inputNumberDecimalPlaces);
// Check if inputNumber is invalid due to having more decimal places
// than the permitted decimal places of the token
console.log(`Invalid?: ${getPrecision(inputNumberAsString) > tokenDecimals}`);
Assuming number is valid.
let number = 0.999;
let noOfPlaces = number.includes(".") //includes or contains
? number.toString().split(".").pop().length
: 0;
5622890.31 ops/s (91.58% slower):
function precision (n) {
return (n.toString().split('.')[1] || '').length
}
precision(1.0123456789)
33004904.53 ops/s (50.58% slower):
function precision (n) {
let e = 1
let p = 0
while(Math.round(n * e) / e !== n) {
e *= 10
p++
}
return p
}
precision(1.0123456789)
62610550.04 ops/s (6.25% slower):
function precision (n) {
let cur = n
let p = 0
while(!Number.isInteger(cur)) {
cur *= 10
p++
}
return p
}
precision(1.0123456789)
66786361.47 ops/s (fastest):
function precision (n) {
let cur = n
let p = 0
while(Math.floor(cur) !== cur) {
cur *= 10
p++
}
return p
}
precision(1.0123456789)
Here is a simple solution
First of all, if you pass a simple float value as 12.1234 then most of the below/above logics may work but if you pass a value as 12.12340, then it may exclude a count of 0. For e.g, if the value is 12.12340 then it may give you a result of 4 instead of 5. As per your problem statement, if you ask javascript to split and count your float value into 2 integers then it won't include trailing 0s of it.
Let's satisfy our requirement here with a trick ;)
In the below function you need to pass a value in string format and it will do your work
function getPrecision(value){
a = value.toString()
console.log('a ->',a)
b = a.split('.')
console.log('b->',b)
return b[1].length
getPrecision('12.12340') // Call a function
For an example, run the below logic
value = '12.12340'
a = value.toString()
b = a.split('.')
console.log('count of trailing decimals->',b[1].length)
That's it! It will give you the exact count for normal float values as well as the float values with trailing 0s!
Thank you!
This answer adds to Mourner's accepted solution by making the function more robust. As noted by many, floating point precision makes such a function unreliable. For example, precision(0.1+0.2) yields 17 rather than 1 (this might be computer specific, but for this example see https://jsfiddle.net/s0v17jby/5/).
IMHO, there are two ways around this: 1. either properly define a decimal type, using e.g. https://github.com/MikeMcl/decimal.js/, or 2. define an acceptable precision level which is both OK for your use case and not a problem for the js Number representation (8 bytes can safely represent a total of 16 digits AFAICT). For the latter workaround, one can write a more robust variant of the proposed function:
const MAX_DECIMAL_PRECISION = 9; /* must be <= 15 */
const maxDecimalPrecisionFloat = 10**MAX_DECIMAL_PRECISION;
function precisionRobust(a) {
if (!isFinite(a)) return 0;
var e = 1, p = 0;
while ( ++p<=MAX_DECIMAL_PRECISION && Math.round( ( Math.round(a * e) / e - a ) * maxDecimalPrecisionFloat ) !== 0) e *= 10;
return p-1;
}
In the above example, the maximum precision of 9 means this accepts up to 6 digits before the decimal point and 9 after (so this would work for numbers less than one million and with a maximum of 9 decimal points). If your use-case numbers are smaller then you can choose to make this precision even greater (but with a maximum of 15). It turns out that, for calculating precision, this function seems to do OK on larger numbers as well (though that would no longer be the case if we were, say, adding two rounded numbers within the precisionRobust function).
Finally, since we now know the maximum useable precision, we can further avoid infinite loops (which I have not been able to replicate but which still seem to cause problems for some).

Round number up to the nearest multiple of 3

How would I go about rounded a number up the nearest multiple of 3?
i.e.
25 would return 27
1 would return 3
0 would return 3
6 would return 6
if(n > 0)
return Math.ceil(n/3.0) * 3;
else if( n < 0)
return Math.floor(n/3.0) * 3;
else
return 3;
Simply:
3.0*Math.ceil(n/3.0)
?
Here you are!
Number.prototype.roundTo = function(num) {
var resto = this%num;
if (resto <= (num/2)) {
return this-resto;
} else {
return this+num-resto;
}
}
Examples:
y = 236.32;
x = y.roundTo(10);
// results in x = 240
y = 236.32;
x = y.roundTo(5);
// results in x = 235
I'm answering this in psuedocode since I program mainly in SystemVerilog and Vera (ASIC HDL). % represents a modulus function.
round_number_up_to_nearest_divisor = number + ((divisor - (number % divisor)) % divisor)
This works in any case.
The modulus of the number calculates the remainder, subtracting that from the divisor results in the number required to get to the next divisor multiple, then the "magic" occurs. You would think that it's good enough to have the single modulus function, but in the case where the number is an exact multiple of the divisor, it calculates an extra multiple. ie, 24 would return 27. The additional modulus protects against this by making the addition 0.
As mentioned in a comment to the accepted answer, you can just use this:
Math.ceil(x/3)*3
(Even though it does not return 3 when x is 0, because that was likely a mistake by the OP.)
Out of the nine answers posted before this one (that have not been deleted or that do not have such a low score that they are not visible to all users), only the ones by Dean Nicholson (excepting the issue with loss of significance) and beauburrier are correct. The accepted answer gives the wrong result for negative numbers and it adds an exception for 0 to account for what was likely a mistake by the OP. Two other answers round a number to the nearest multiple instead of always rounding up, one more gives the wrong result for negative numbers, and three more even give the wrong result for positive numbers.
This function will round up to the nearest multiple of whatever factor you provide.
It will not round up 0 or numbers which are already multiples.
round_up = function(x,factor){ return x - (x%factor) + (x%factor>0 && factor);}
round_up(25,3)
27
round up(1,3)
3
round_up(0,3)
0
round_up(6,3)
6
The behavior for 0 is not what you asked for, but seems more consistent and useful this way. If you did want to round up 0 though, the following function would do that:
round_up = function(x,factor){ return x - (x%factor) + ( (x%factor>0 || x==0) && factor);}
round_up(25,3)
27
round up(1,3)
3
round_up(0,3)
3
round_up(6,3)
6
Building on #Makram's approach, and incorporating #Adam's subsequent comments, I've modified the original Math.prototype example such that it accurately rounds negative numbers in both zero-centric and unbiased systems:
Number.prototype.mround = function(_mult, _zero) {
var bias = _zero || false;
var base = Math.abs(this);
var mult = Math.abs(_mult);
if (bias == true) {
base = Math.round(base / mult) * _mult;
base = (this<0)?-base:base ;
} else {
base = Math.round(this / _mult) * _mult;
}
return parseFloat(base.toFixed(_mult.precision()));
}
Number.prototype.precision = function() {
if (!isFinite(this)) return 0;
var a = this, e = 1, p = 0;
while (Math.round(a * e) / e !== a) { a *= 10; p++; }
return p;
}
Examples:
(-2).mround(3) returns -3;
(0).mround(3) returns 0;
(2).mround(3) returns 3;
(25.4).mround(3) returns 24;
(15.12).mround(.1) returns 15.1
(n - n mod 3)+3
$(document).ready(function() {
var modulus = 3;
for (i=0; i < 21; i++) {
$("#results").append("<li>" + roundUp(i, modulus) + "</li>")
}
});
function roundUp(number, modulus) {
var remainder = number % modulus;
if (remainder == 0) {
return number;
} else {
return number + modulus - remainder;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Round up to nearest multiple of 3:
<ul id="results">
</ul>
A more general answer that might help somebody with a more general problem: if you want to round numbers to multiples of a fraction, consider using a library. This is a valid use case in GUI where decimals are typed into input and for instance you want to coerce them to multiples of 0.25, 0.2, 0.5 etc. Then the naive approach won't get you far:
function roundToStep(value, step) {
return Math.round(value / step) * step;
}
console.log(roundToStep(1.005, 0.01)); // 1, and should be 1.01
After hours of trying to write up my own function and looking up npm packages, I decided that Decimal.js gets the job done right away. It even has a toNearest method that does exactly that, and you can choose whether to round up, down, or to closer value (default).
const Decimal = require("decimal.js")
function roundToStep (value, step) {
return new Decimal(value).toNearest(step).toNumber();
}
console.log(roundToStep(1.005, 0.01)); // 1.01
RunKit example
Using remainder operator (modulus):
(n - 1 - (n - 1) % 3) + 3
By the code given below use can change any numbers and you can find any multiple of any number
let numbers = [8,11,15];
let multiple = 3
let result = numbers.map(myFunction);
function myFunction(n){
let answer = Math.round(n/multiple) * multiple ;
if (answer <= 0)
return multiple
else
return answer
}
console.log("Closest Multiple of " + multiple + " is " + result);
if(x%3==0)
return x
else
return ((x/3|0)+1)*3

Categories

Resources