Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you round a number in JavaScript in a base other than 10?

For example, let's say I have the number 12.24 in base 8 (10.3125 in base 10), and I want to round the number to 1 point of precision so that I get 12.3.

like image 427
Steven Jeffries Avatar asked Jul 21 '21 20:07

Steven Jeffries


People also ask

How do you round to 5 in JavaScript?

To round a number to the nearest 5, call the Math. round() function, passing it the number divided by 5 and multiply the result by 5 .

How do you round a value in JavaScript?

The Math. round() method rounds a number to the nearest integer. 2.49 will be rounded down (2), and 2.5 will be rounded up (3).

How do you round the number 7.25 to the nearest integer in JavaScript?

The Math. round() function in JavaScript is used to round the number passed as parameter to its nearest integer. Parameters : The number to be rounded to its nearest integer.

How do I round to 2 decimal places in JavaScript?

Use the toFixed() method to round a number to 2 decimal places, e.g. const result = num. toFixed(2) . The toFixed method will round and format the number to 2 decimal places.

How to round to a certain number of digits in JavaScript?

T here is no built-in method to round to a certain number of digits in JavaScript. You might think the Math.round () function would take an argument specifying a desired precision, but it doesn’t. “The Math.round () function returns the value of a number rounded to the nearest integer.” — MDN Docs

Can You Round Up or down decimal places in JavaScript?

Unfortunately, you can actually run into the same issue with a positive number of decimal places, where 0.5 is occasionally rounded down instead of up, as you can see in the following example: What just happened? Floating point math in JavaScript can produce rounding errors at times, as I discussed in my article in JavaScript in Plain English:

How do you round up and down in math?

The first number rounds up since it above .5. The second number rounds down since it is below .5. Math.floor rounds the number down to the nearest whole number no matter what the decimals are. Here both numbers round down to 5.

How to avoid rounding errors in JavaScript?

You just need to be aware of JavaScript’s inherent floating point rounding errors, which are usually not too serious. You can avoid rounding errors completely by using exponential notation when rounding. This tutorial only addressed arithmetic rounding, not creating a string from a number using a certain number of decimal places.


Video Answer


1 Answers

Solution 1

  • There isn´t a function in js that can do this. So need to create one in order to do it. What it does is to check if the digit to fix (in your case the "4") is closer to the base (8) or closer to 0:

    if (base - digit_tofix <= base / 2) {
    

Then, just add or substract the difference to 10 or 0, so if the number is 12.24, it would return:

return 12.24 + 0.06;   // ---> equals to 12.3

If the number is 12.23, returns:

return 12.23 - 0.03    // ---> equals to 12.2

So could make something like this:

function round(n, base, precision) {
  const digit_tofix = n.toString().split(".")[1].split("")[precision];
  if (base - digit_tofix <= base / 2) {
    return (n + (10 - digit_tofix) / Math.pow(10, precision + 1)).toFixed(precision);
  }else {
    return (n - digit_tofix / Math.pow(10, precision + 1)).toFixed(precision);
  }
}

EDIT: however, this wouldn´t catch carry overflow. So a number in base_8 like this:

55.74

would transform in:

55.74 + 0.06 = 55.8   // --> number 8 doesn´t exist in octal

So there must be an overflow-check loop using do while(). What this statement do is to move the digit_tofix to the previous number and check if there is an overflow every time we make an addition:

// Overflow check
do {
    n += (10 - digit_tofix) / Math.pow(10, precision + 1);
    precision--;
    digit_tofix = precision > -1 ? n.toFixed(precision + 1).split(".")[1].split("")[precision] :
                                   n.toFixed(0).split(".")[0].split("")[precision + n.toString().split(".")[0].split("").length];
} while(digit_tofix == base);
    
return n.toFixed(precision > -1 ? precision + 1 : 0);
  • It works for any base up to 10 (not hexadecimal since contains letters) and any precision you want, just changing the parameters in the round() function. Here some working examples

Octal

const oct = 12.24;

console.log( round(oct, 8, 1) );

function round(n, base, precision) {
  let digit_tofix = n.toString().split(".")[1].split("")[precision];
  if (base - digit_tofix <= base / 2) {
    // Overflow check
    do {
        n += (10 - digit_tofix) / Math.pow(10, precision + 1);
        precision--;
        digit_tofix = precision > -1 ? n.toFixed(precision + 1).split(".")[1].split("")[precision] :
                                       n.toFixed(0).split(".")[0].split("")[precision + n.toString().split(".")[0].split("").length];
    } while(digit_tofix == base);
    return n.toFixed(precision > -1 ? precision + 1 : 0);
  }else {
    return (n - digit_tofix / Math.pow(10, precision + 1)).toFixed(precision);
  }
}

Binary

const bin = 10011.011;

console.log( round(bin, 2, 1) );

function round(n, base, precision) {
  let digit_tofix = n.toString().split(".")[1].split("")[precision];
  if (base - digit_tofix <= base / 2) {
    // Overflow check
    do {
        n += (10 - digit_tofix) / Math.pow(10, precision + 1);
        precision--;
        digit_tofix = precision > -1 ? n.toFixed(precision + 1).split(".")[1].split("")[precision] :
                                       n.toFixed(0).split(".")[0].split("")[precision + n.toString().split(".")[0].split("").length];
    } while(digit_tofix == base);
    return n.toFixed(precision > -1 ? precision + 1 : 0);
  }else {
    return (n - digit_tofix / Math.pow(10, precision + 1)).toFixed(precision);
  }
}

Solution 2

function round(n, base, precision) {
  // To get number value of letter: a = 10, b = 11, c = 12 ...
  const getNumber = e => isNaN(e) ? e.charCodeAt(0) - 87 : parseInt(e);

  // Just to check incompability and invalid parameters
  const checkIncompatible = () => {
    if (digits.reduce((a, d) => d == "." ? ++a : a, 0) > 1 || 
        (digits.some(d => isNaN(d) && (getNumber(d) < 10 || getNumber(d) > 35) && d != ".")))   return "Invalid Number";
    if (digits.some(d => getNumber(d) >= base  && d != "."))    return "Number doesn´t match base";
    if (precision < 0)  return "Invalid precision argument";
    if (base < 2 || base > 36)  return "Invalid base argument";
    return false;
  };
  
  // Recursive function to carry overflow
  const add = p => {
    if (digits[p] == ".") add(p - 1);
    else if (getNumber(digits[p]) + 1 == base) {
      digits[p] = "0";
      if (p > 0)    add(p - 1);
      else digits.unshift("1");
    }else {
      if (isNaN(digits[p])) digits[p] = String.fromCharCode(digits[p].charCodeAt(0) + 1);
      else digits[p] = digits[p] < 9 ? parseInt(digits[p]) + 1 : "a" ;
    }
  };
  
  n = n.toString().toLowerCase().split(",").join(".");  // In case there is a comma
  let digits = n.split("");

  // Avoid problems with undefined array index and other issues
  if (typeof digits[digits.indexOf(".") + 1 + precision] === "undefined" ||
      digits.reduce((a, d) => d == "." ? ++a : a, 0) < 1)   return n;
  let state = checkIncompatible();
  if (state) return state;

  const digit_tofix = getNumber(digits[digits.indexOf(".") + 1 + precision]);

  // Check if digit_tofix is closer to base or zero
  if (base - digit_tofix <= base / 2) {
   add(digits.indexOf(".") + precision);
  }

  // Splice the array to get the substring
  digits.splice(digits.indexOf(".") + 1 + precision);
  if (!precision) digits.splice(-1);
  return digits.join("");
}

This solution is more complex, but is broader since you can use any number from base 2 to 36, it means you can use hexadecimal.

Similarly to first solution, it checks if the digit to fix (in your case the "4") is closer to the base (8) or closer to 0:

if (base - digit_tofix <= base / 2) {

If is closer to zero, just need to take the substring of the number (using splice() array method). So if number is:

"12.23"

its substring is

"12.2"

But if digit-to-fix is closer to the base, we need to add +1 to the previous number, so if number is:

"12.24"

its substring is

"12.3"    //--> 2 + 1 = 3

Special cases

For example, if number in base_8 is:

"12.74"

its substring should be

"13.0"    // 7 + 1 = 8 (base is 8 so there is an overflow)

That´s why there is a recursive function called add() in order to carry in case of overflow.

The round() function returns a string with the new number.

Try the solution

Hexadecimal

console.log( round("c3.bf9", 16, 2) );
console.log( round("AA.D1", 16, 1) );
console.log( round("ff.fff", 16, 0) );

function round(n, base, precision) {
    const getNumber = e => isNaN(e) ? e.charCodeAt(0) - 87 : parseInt(e);
  const checkIncompatible = () => {
    if (digits.reduce((a, d) => d == "." ? ++a : a, 0) > 1 || 
        (digits.some(d => isNaN(d) && (getNumber(d) < 10 || getNumber(d) > 35) && d != ".")))   return "Invalid Number";
    if (digits.some(d => getNumber(d) >= base  && d != "."))    return "Number doesn´t match base";
    if (precision < 0)  return "Invalid precision argument";
    if (base < 2 || base > 36)  return "Invalid base argument";
    return false;
  };
  
  const add = p => {
    if (digits[p] == ".") add(p - 1);
    else if (getNumber(digits[p]) + 1 == base) {
      digits[p] = "0";
      if (p > 0)    add(p - 1);
      else digits.unshift("1");
    }else {
      if (isNaN(digits[p])) digits[p] = String.fromCharCode(digits[p].charCodeAt(0) + 1);
      else digits[p] = digits[p] < 9 ? parseInt(digits[p]) + 1 : "a" ;
    }
  };
  
  n = n.toString().toLowerCase().split(",").join(".");
  let digits = n.split("");
  if (typeof digits[digits.indexOf(".") + 1 + precision] === "undefined" ||
      digits.reduce((a, d) => d == "." ? ++a : a, 0) < 1)   return n;
  let state = checkIncompatible();
  if (state) return state;
  const digit_tofix = getNumber(digits[digits.indexOf(".") + 1 + precision]);
  if (base - digit_tofix <= base / 2) {
   add(digits.indexOf(".") + precision);
  }
  digits.splice(digits.indexOf(".") + 1 + precision);
  if (!precision) digits.splice(-1);
  return digits.join("");
}

Octal

console.log( round(12.24, 8, 1) );
console.log( round(17.77, 8, 1) );
console.log( round(0.74, 8, 1) );

function round(n, base, precision) {
    const getNumber = e => isNaN(e) ? e.charCodeAt(0) - 87 : parseInt(e);
  const checkIncompatible = () => {
    if (digits.reduce((a, d) => d == "." ? ++a : a, 0) > 1 || 
        (digits.some(d => isNaN(d) && (getNumber(d) < 10 || getNumber(d) > 35) && d != ".")))   return "Invalid Number";
    if (digits.some(d => getNumber(d) >= base  && d != "."))    return "Number doesn´t match base";
    if (precision < 0)  return "Invalid precision argument";
    if (base < 2 || base > 36)  return "Invalid base argument";
    return false;
  };
  
  const add = p => {
    if (digits[p] == ".") add(p - 1);
    else if (getNumber(digits[p]) + 1 == base) {
      digits[p] = "0";
      if (p > 0)    add(p - 1);
      else digits.unshift("1");
    }else {
      if (isNaN(digits[p])) digits[p] = String.fromCharCode(digits[p].charCodeAt(0) + 1);
      else digits[p] = digits[p] < 9 ? parseInt(digits[p]) + 1 : "a" ;
    }
  };
  
  n = n.toString().toLowerCase().split(",").join(".");
  let digits = n.split("");
  if (typeof digits[digits.indexOf(".") + 1 + precision] === "undefined" ||
      digits.reduce((a, d) => d == "." ? ++a : a, 0) < 1)   return n;
  let state = checkIncompatible();
  if (state) return state;
  const digit_tofix = getNumber(digits[digits.indexOf(".") + 1 + precision]);
  if (base - digit_tofix <= base / 2) {
   add(digits.indexOf(".") + precision);
  }
  digits.splice(digits.indexOf(".") + 1 + precision);
  if (!precision) digits.splice(-1);
  return digits.join("");
}

Binary

console.log( round(101.10, 2, 1) );
console.log( round("100,11", 2, 1) );
console.log( round(100.11, 2, 1) );

function round(n, base, precision) {
    const getNumber = e => isNaN(e) ? e.charCodeAt(0) - 87 : parseInt(e);
  const checkIncompatible = () => {
    if (digits.reduce((a, d) => d == "." ? ++a : a, 0) > 1 || 
        (digits.some(d => isNaN(d) && (getNumber(d) < 10 || getNumber(d) > 35) && d != ".")))   return "Invalid Number";
    if (digits.some(d => getNumber(d) >= base  && d != "."))    return "Number doesn´t match base";
    if (precision < 0)  return "Invalid precision argument";
    if (base < 2 || base > 36)  return "Invalid base argument";
    return false;
  };
  
  const add = p => {
    if (digits[p] == ".") add(p - 1);
    else if (getNumber(digits[p]) + 1 == base) {
      digits[p] = "0";
      if (p > 0)    add(p - 1);
      else digits.unshift("1");
    }else {
      if (isNaN(digits[p])) digits[p] = String.fromCharCode(digits[p].charCodeAt(0) + 1);
      else digits[p] = digits[p] < 9 ? parseInt(digits[p]) + 1 : "a" ;
    }
  };
  
  n = n.toString().toLowerCase().split(",").join(".");
  let digits = n.split("");
  if (typeof digits[digits.indexOf(".") + 1 + precision] === "undefined" ||
      digits.reduce((a, d) => d == "." ? ++a : a, 0) < 1)   return n;
  let state = checkIncompatible();
  if (state) return state;
  const digit_tofix = getNumber(digits[digits.indexOf(".") + 1 + precision]);
  if (base - digit_tofix <= base / 2) {
   add(digits.indexOf(".") + precision);
  }
  digits.splice(digits.indexOf(".") + 1 + precision);
  if (!precision) digits.splice(-1);
  return digits.join("");
}
like image 86
AlexSp3 Avatar answered Oct 21 '22 23:10

AlexSp3