-1

I am trying to round numbers to a certain decimal place, the expected API would be something like this:

const rounded = Math.round(1.6180339, 5);

or

const rounded = new Number(1.6180339).round(5);

but those APIs don't seem to exist. I have this which appears to work as is:

const [
  e,
  π,
  φ
] = [
  2.71828182845904,
  3.14159265358979,
  1.61803398874989,
];


console.log(
  Number(e.toFixed(5)) === 2.71828  // true
);

console.log(
  Number(e.toFixed(3)) === 2.718    // true
);

console.log(
  Number(e.toFixed(3)) === 2.7182   // false
);

console.log(
  Number(e.toFixed(3)) === 2.71     // false
);

this works, but we have to use toFixed() which converts number to a string first. Is there a way to round a number directly without converting to a string?

Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
  • you might be interested to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor but keep in mind that Number is a floating point number and won't always fit with the mathematics – Diego D Dec 16 '22 at 09:27
  • Math.floor and Math.ceil appear to only round to integers.. what a f*cken nightmare, did you see this section? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor#decimal_adjustment – Alexander Mills Dec 16 '22 at 09:30
  • 1
    numbers in js are a big headache.. the floating point part being the biggest. One of the reason why there's also BigInt https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt – Diego D Dec 16 '22 at 09:33
  • thanks, no problem, definitely agree, BigInt is a good idea – Alexander Mills Dec 16 '22 at 09:33
  • 1
    You first need to understand how floating point numbers work. It's similar to decimal numbers where each digit behind the decimal point is 1/10 smaller than the previous digit (that is 0.123 means 1/10 + 2/100 + 3/1000), each **bit** behind the decimal is 1/2 smaller than the previous bit. In a similar way you can't exactly represent 1/3 in decimal, you cannot quite represent some decimal numbers in floating point format. The famous one is 0.1 cannot be exactly represented by floating point (it must be approximated by 1/16 + 1/32 + 1/256 + 1/512 + 1/4096 + 1/8192 + 1/65536 etc.) – slebetman Dec 16 '22 at 10:20
  • @slebetman I don't see how that would be a problem for rounding numbers, do you? – Alexander Mills Dec 16 '22 at 10:22
  • Say for example you want to round `0.1227622` to one decimal place. You seem to expect `0.1` but that result is only possible in string format. In floating point format 0.1 requires an infinite number of bits, the same way 1/3 requires an infinite number of digits for decimal numbers (0.33333... to infinity). – slebetman Dec 16 '22 at 10:25
  • @slebetman well, I guess rounding numbers is impossible in JavaScript then – Alexander Mills Dec 16 '22 at 10:28
  • 1
    It's not a problem specific with javascript. It's a problem with commonly used CPUs. Floating point numbers is not a feature of programming language, it is a feature of hardware. There are languages out there that implement decimal numbers not using floating point format but that means you're deliberately avoiding using the hardware to do math. Some specialized languages actually use what is basically string manipulation to do math (except they usually do it 4 bits per digit instead of a byte per digit so one byte stores two digits)... – slebetman Dec 16 '22 at 13:20
  • 1
    .. such languages work great for accounting because they deal with numbers similar to how humans work with numbers but are generally quite slow. Common languages like javascript, Java, C++, Python, Ruby, Go etc. use floating point numbers for speed and assume the programmer can learn how to use the right algorithms when working with money. For anything that is not money related the way floating point numbers work is more than good enough. – slebetman Dec 16 '22 at 13:22

1 Answers1

1

Like mentioned in comments, floating point operations can be a pain with javascript. Anyway, you can still build a tool like this one that relies on powers of 10 to perform roundings and avoid string conversion step :

const Rounder = {
  floor(n, m) {
      return Math.floor(n * Math.pow(10, m)) / Math.pow(10, m);
  },
  ceil(n, m) {
      return Math.ceil(n * Math.pow(10, m)) / Math.pow(10, m);
  },
  round(n, m) {
      return Math.round(n * Math.pow(10, m)) / Math.pow(10, m);
  }
}

console.log(Rounder.ceil(7.1812828, 3));
console.log(Rounder.floor(7.1812828, 5));
console.log(Rounder.round(7.1812828, 2));
console.log(Rounder.round(0.11111111111, 8));
console.log(Rounder.ceil(0.11111111111, 8));
Bertrand
  • 1,840
  • 14
  • 22
  • thanks, for better or worse, I presume using `Number(x.toFixed(y))` is faster and just as precise? might be nice to test it out – Alexander Mills Dec 16 '22 at 10:41
  • 1
    It seems to be amazingly fast with Math.pow. I've tried on https://jsbench.me and results are 842,907,326 ops/s with Math.pow and 3,888,108 ops/s with toFixed (99.54% slower). Furthermore, you can choose your rounding behavior where toFixed doesn't allow it. – Bertrand Dec 16 '22 at 12:45