如何在 JavaScript 中将数字舍入到除10以外的基数?

7
例如,假设我在8进制中有数字12.24(在10进制中为10.3125 ),我想将这个数字舍入到1位小数,以便得到12.3

1
肯定没有内置的方法来做到这一点。我建议尝试捕获小数点后的 n 位数字,然后检查第 n+1 位数字是否为 4、5、6 或 7,以有条件地增加前一位数字。(或者对于一般的基数 B,高于 Math.ceil(B/2)?我不知道如何处理奇数基数。)不要使用任何数字运算(除了在数字级别上进行比较和递增),因为你只是进行字符串到字符串的转换。 - apsillers
((10.3125 * 32) & 7) > 4 ? ((10.3125 * 8) >> 0) / 8 + 16 :((10.3215 * 8) >> 0) / 8 - Jonas Wilms
12.24不应该四舍五入为12.2吗? - The Bomb Squad
1
"我想将数字四舍五入到小数点后一位": 嗯,如果是八进制表示,则该位置不是“十进制”位... - trincot
1
你可以非常精细地实现基于 8 的家庭银行家舍入,这样 12.24 就会被舍入为 12.2,但 12.34 就会舍入为 12.4。 - Don R
显示剩余5条评论
1个回答

3

解决方案 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) {
    

然后,只需将差值加或减到10或0,因此如果数字是12.24,则会返回:

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

如果数字为12.23,返回:
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);
  }
}

编辑: 但是,这种方法无法捕获进位溢出。因此,像这样的八进制数字:

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);
  • 该函数适用于任何进制(不包括包含字母的十六进制)以及任何所需精度,只需更改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"

但如果将数字转换为固定位数的数字时,该数字更接近进制,我们需要在前一个数字上加+1。因此,如果数字为:

"12.24"

它的子字符串是

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

特殊情况

例如,如果 八进制 数字为:

"12.74"

它的子字符串应该是:

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

因此,存在一个名为 add() 的递归函数,以便在溢出的情况下进行处理。

round() 函数返回一个新数字的字符串。

尝试解决方案

十六进制

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接