如何以 10 以外的基数对 JavaScript 中的数字进行四舍五入?

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

例如,假设我有一个以 8 为底的数字 12.24(以 10 为底的 10.3125),我想将该数字四舍五入到 1 点精度,以便我得到 12.3.

解决方案 1

  • js 中没有可以做到这一点的函数。所以需要创建一个才能做到这一点。它的作用是检查要修复的数字(在您的情况下为“4”)是否更接近基数 (8) 或更接近 0:

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

然后,只需将差值加或减到 10 或 0,所以如果数字是 12.24,它会 return:

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

如果号码是12.23,returns:

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

所以可以做这样的东西:

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);
  }
}

编辑: 但是,这不会捕获 进位溢出 。所以 base_8 中的数字是这样的:

55.74

将转换为:

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

所以必须有一个溢出检查循环使用do while()。这条语句的作用是将digit_tofix移动到之前的数字,每次加法检查是否溢出:

// 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);
  • 它适用于最多 10 的任何基数(不是十六进制,因为包含字母)和您想要的任何精度,只需更改 round() 函数中的参数即可。这里有一些工作示例

八进制

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);
  }
}

二进制

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);
  }
}


解决方案 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("");
}

此解决方案更复杂,但范围更广,因为您可以使用从 2 到 36 的任何数字,这意味着您可以使用十六进制。

与第一个解决方案类似,它检查要修复的数字(在您的情况下为“4”)是更接近基数 (8) 还是更接近 0:

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

如果接近,只需要取数字的子串即可(使用splice()数组法)。所以如果数字是:

"12.23"

它的子串是

"12.2"

但是如果digit-to-fix更接近base,我们需要在前面的数字上加上+1,所以如果数字是:

"12.24"

它的子串是

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

特例

例如,如果 base_8 中的数字是:

"12.74"

它的子串应该是

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

这就是为什么有一个称为 add() 的递归函数,以便在溢出的情况下执行。

round() 函数 return 是一个带有新数字的字符串。

尝试解决方案

十六进制

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("");
}

八进制

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("");
}

二进制

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("");
}