josdejong / mathjs

An extensive math library for JavaScript and Node.js
https://mathjs.org
Apache License 2.0
14.4k stars 1.24k forks source link

`math.format()` and trailing zeros after decimal point in fixed notation #2079

Open balintbrews opened 3 years ago

balintbrews commented 3 years ago

When precision is set to a value larger than 0, math.format() with the fixed notation appends trailing zeros after the decimal point when the value is an integer: https://github.com/josdejong/mathjs/blob/develop/src/utils/number.js#L353.

Would you be open to a new option to disable this behavior? I tried doing this with a custom format callback, but I quickly ended up copy-pasting hundreds of lines from the library code, because I also didn't want to lose the lots of great things math.format() is doing.

I would be happy to work on a PR to introduce a new option if you think it's a good idea.

What I'm after is exactly what the fixed notation gives me, but I would like to skip adding zeros to integers when I have the precision set to, say, 2, which will give me nicely rounded values after the decimal point. Currently the closest I can get to this is leaving the notation to use auto and set high enough lowerExp and upperExp values. This removes trailing zeros, but due to the different behavior of precision with this notation I can't do rounding only for decimal digits.

josdejong commented 3 years ago

There are a lot of different combinations of user needs for either significant digits, digits after the comma, trailing zeros or not, etc. The function math.format can handle many of them nicely but there is indeed still cases that are not easy to address.

I think causes are:

What do you think would be a good solution? Introduce a new option trailingZeros: boolean?

A simple workaround would be to write a little regular expression to strip trailing zeros.

PaulRBerg commented 3 years ago

What do you think would be a good solution? Introduce a new option trailingZeros: boolean?

That would be nice. I just ran into the same issue and I think currently the only solution is to either use a third-party module, or write a custom function that strips the trailing zeros.

PaulRBerg commented 3 years ago

Sharing the solution I ended up creating, for posterity:

import { Decimal } from "decimal.js";

declare module "decimal.js" {
  interface Decimal {
    toFixedWithNoTrailingZeros(): string;
  }
}

// See https://stackoverflow.com/a/65172703/3873510
Decimal.prototype.toFixedWithNoTrailingZeros = function (decimals = 18) {
  return this.toFixed(decimals).replace(/(\.0*|(?<=(\..*))0*)$/, "");
};

Which you use like this:

const result = bignumber!("3.1400");
console.log(result.toFixedWithNoTrailingZeros());
josdejong commented 3 years ago

Thanks for sharing your solution Paul 👍