简体   繁体   中英

Remove insignificant trailing zeros from a number?

Have I missed a standard API call that removes trailing insignificant zeros from a number?

var x = 1.234000; // to become 1.234
var y = 1.234001; // stays 1.234001

Number.toFixed() and Number.toPrecision() are not quite what I'm looking for.

I had a similar instance where I wanted to use .toFixed() where necessary, but I didn't want the padding when it wasn't. So I ended up using parseFloat in conjunction with toFixed.

toFixed without padding

parseFloat(n.toFixed(4));

Another option that does almost the same thing
This answer may help your decision

Number(n.toFixed(4));

toFixed will round/pad the number to a specific length, but also convert it to a string. Converting that back to a numeric type will not only make the number safer to use arithmetically, but also automatically drop any trailing 0's. For example:

var n = "1.234000";
    n = parseFloat(n);
 // n is 1.234 and in number form

Because even if you define a number with trailing zeros they're dropped.

var n = 1.23000;
 // n == 1.23;

If you convert it to a string it will not display any trailing zeros, which aren't stored in the variable in the first place since it was created as a Number, not a String.

var n = 1.245000
var noZeroes = n.toString() // "1.245" 

I first used a combination of matti-lyra and gary's answers:

r=(+n).toFixed(4).replace(/\.0+$/,'')

Results:

  • 1234870.98762341: "1234870.9876"
  • 1230009100: "1230009100"
  • 0.0012234: "0.0012"
  • 0.1200234: "0.12"
  • 0.000001231: "0"
  • 0.10001: "0.1000"
  • "asdf": "NaN" (so no runtime error)

The somewhat problematic case is 0.10001. I ended up using this longer version:

    r = (+n).toFixed(4);
    if (r.match(/\./)) {
      r = r.replace(/\.?0+$/, '');
    }
  • 1234870.98762341: "1234870.9876"
  • 1230009100: "1230009100"
  • 0.0012234: "0.0012"
  • 0.1200234: "0.12"
  • 0.000001231: "0"
  • 0.10001: "0.1"
  • "asdf": "NaN" (so no runtime error)

Update : And this is Gary's newer version (see comments):

r=(+n).toFixed(4).replace(/([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/,'$1')

This gives the same results as above.

The toFixed method will do the appropriate rounding if necessary. It will also add trailing zeroes, which is not always ideal.

(4.55555).toFixed(2);
//-> "4.56"

(4).toFixed(2);
//-> "4.00"

If you cast the return value to a number, those trailing zeroes will be dropped. This is a simpler approach than doing your own rounding or truncation math.

+(4.55555).toFixed(2);
//-> 4.56

+(4).toFixed(2);
//-> 4

How about just multiplying by one like this?

var x = 1.234000*1; // becomes 1.234

var y = 1.234001*1; // stays as 1.234001

I had the basically the same requirement, and found that there is no built-in mechanism for this functionality.

In addition to trimming the trailing zeros, I also had the need to round off and format the output for the user's current locale (ie 123,456.789).

All of my work on this has been included as prettyFloat.js (MIT Licensed) on GitHub: https://github.com/dperish/prettyFloat.js


Usage Examples:

prettyFloat(1.111001, 3) // "1.111"
prettyFloat(1.111001, 4) // "1.111"
prettyFloat(1.1111001, 5) // "1.1111"
prettyFloat(1234.5678, 2) // "1234.57"
prettyFloat(1234.5678, 2, true) // "1,234.57" (en-us)


Updated - August, 2018


All modern browsers now support the ECMAScript Internationalization API , which provides language sensitive string comparison, number formatting, and date and time formatting.

let formatters = {
    default: new Intl.NumberFormat(),
    currency: new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 0, maximumFractionDigits: 0 }),
    whole: new Intl.NumberFormat('en-US', { style: 'decimal', minimumFractionDigits: 0, maximumFractionDigits: 0 }),
    oneDecimal: new Intl.NumberFormat('en-US', { style: 'decimal', minimumFractionDigits: 1, maximumFractionDigits: 1 }),
    twoDecimal: new Intl.NumberFormat('en-US', { style: 'decimal', minimumFractionDigits: 2, maximumFractionDigits: 2 })
};

formatters.twoDecimal.format(1234.5678);  // result: "1,234.57"
formatters.currency.format(28761232.291); // result: "$28,761,232"

For older browsers, you can use this polyfill: https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.en

You can try this one to minify floating numbers

var n = 0.0000;
n = parseFloat(n.toString()); 

//output n = 0; 
// n = 3.14000; --> n = 3.14;

Pure regex answer

n.replace(/(\.[0-9]*[1-9])0+$|\.0*$/,'$1');

I wonder why no one gave one!

I needed to solve this problem too when Django was displaying Decimal type values in a text field. Eg when '1' was the value. It would show '1.00000000'. If '1.23' was the value, it would show '1.23000000' (In the case of a 'decimal_places' setting of 8)

Using parseFloat was not an option for me since it is possible it does not return the exact same value. toFixed was not an option since I did not want to round anything, so I created a function:

function removeTrailingZeros(value) {
    value = value.toString();

    # if not containing a dot, we do not need to do anything
    if (value.indexOf('.') === -1) {
        return value;
    }

    # as long as the last character is a 0 or a dot, remove it
    while((value.slice(-1) === '0' || value.slice(-1) === '.') && value.indexOf('.') !== -1) {
        value = value.substr(0, value.length - 1);
    }
    return value;
}

None of these solutions worked for me for very small numbers. http://numeraljs.com/ solved this for me.

parseFloat(0.00000001.toFixed(8));
// 1e-8

numeral(0.00000001).format('0[.][00000000]');
// "0.00000001"

If you use toFixed(n) where n > 0, a more simple and stable (no more float operations) solution can be:

(+n).toFixed(2).replace(/(\.0+|0+)$/, '')


// 0 => 0
// 0.1234 => 0.12
// 0.1001 => 0.1

// 1 => 1
// 1.1234 => 1.12
// 1.1001 => 1.1

// 100 => 100
// 100.1234 => 100.12
// 100.1001 => 100.1

PS: if you use toFixed(0) , then no replace is needed.

If you cannot use Floats for any reason (like money-floats involved) and are already starting from a string representing a correct number, you could find this solution handy. It converts a string representing a number to a string representing number w/out trailing zeroes.

function removeTrailingZeroes( strAmount ) {
    // remove all trailing zeroes in the decimal part
    var strDecSepCd = '.'; // decimal separator
    var iDSPosition = strAmount.indexOf( strDecSepCd ); // decimal separator positions
    if ( iDSPosition !== -1 ) {
        var strDecPart = strAmount.substr( iDSPosition ); // including the decimal separator

        var i = strDecPart.length - 1;
        for ( ; i >= 0 ; i-- ) {
            if ( strDecPart.charAt(i) !== '0') {
                break;
            }
        }

        if ( i=== 0 ) {
            return strAmount.substring(0, iDSPosition);
        } else {
            // return INTPART and DS + DECPART including the rightmost significant number
            return strAmount.substring(0, iDSPosition) + strDecPart.substring(0,i + 1);
        }
    }

    return strAmount;
}

If we have some s string representation of a number, which we can get for example using the .toFixed(digits) method of Number (or by any other means), then for removal of insignificant trailing zeros from the s string we can use:

s.replace(/(\.0*|(?<=(\..*))0*)$/, '')

/**********************************
 * Results for various values of s:
 **********************************
 *
 * "0" => 0
 * "0.000" => 0
 * 
 * "10" => 10
 * "100" => 100
 * 
 * "0.100" => 0.1
 * "0.010" => 0.01
 * 
 * "1.101" => 1.101
 * "1.100" => 1.1
 * "1.100010" => 1.10001
 * 
 * "100.11" => 100.11
 * "100.10" => 100.1
 */

Regular expression used above in the replace() is explained below:

  • In the first place please pay the attention to the | operator inside the regular expression, which stands for "OR", so, the replace() method will remove from s two possible kinds of substring, matched either by the (\\.0*)$ part OR by the ((?<=(\\..*))0*)$ part.
  • The (\\.0*)$ part of regex matches a dot symbol followed by all the zeros and nothing else till to the end of the s . This might be for example 0.0 ( .0 is matched & removed), 1.0 ( .0 is matched & removed), 0.000 ( .000 is matched & removed) or any similar string with all the zeros after the dot, so, all the trailing zeros and the dot itself will be removed if this part of regex will match.
  • The ((?<=(\\..*))0*)$ part matches only the trailing zeros (which are located after a dot symbol followed by any number of any symbol before start of the consecutive trailing zeros). This might be for example 0.100 (trailing 00 is matched & removed), 0.010 (last 0 is matched & removed, note that 0.01 part do NOT get matched at all thanks to the "Positive Lookbehind Assertion", ie (?<=(\\..*)) , which is in front of 0* in this part of regex), 1.100010 (last 0 is matched & removed), etc.
  • If neither of the two parts of expression will match, nothing gets removed. This might be for example 100 or 100.11 , etc. So, if an input does not have any trailing zeros then it stays unchanged.

Some more examples using .toFixed(digits) (Literal value "1000.1010" is used in the examples below, but we can assume variables instead):

let digits = 0; // Get `digits` from somewhere, for example: user input, some sort of config, etc.

(+"1000.1010").toFixed(digits).replace(/(\.0*|(?<=(\..*))0*)$/, '');
// Result: '1000'

(+"1000.1010").toFixed(digits = 1).replace(/(\.0*|(?<=(\..*))0*)$/, '');
// Result: '1000.1'


(+"1000.1010").toFixed(digits = 2).replace(/(\.0*|(?<=(\..*))0*)$/, '');
// Result: '1000.1'


(+"1000.1010").toFixed(digits = 3).replace(/(\.0*|(?<=(\..*))0*)$/, '');
// Result: '1000.101'


(+"1000.1010").toFixed(digits = 4).replace(/(\.0*|(?<=(\..*))0*)$/, '');
// Result: '1000.101'


(+"1000.1010").toFixed(digits = 5).replace(/(\.0*|(?<=(\..*))0*)$/, '');
// Result: '1000.101'

(+"1000.1010").toFixed(digits = 10).replace(/(\.0*|(?<=(\..*))0*)$/, '');
// Result: '1000.101'

To play around with the above regular expression used in replace() we can visit: https://regex101.com/r/owj9fz/1

This is how I do it:

parseFloat(number.toString());

This is a good workaround for the TypeScript bug too. The bug that changes number to string in certain situations.

So you want

var x = 1.234000; // to become 1.234
var y = 1.234001; // stays 1.234001

No string s attached, just try Number() .

 var x = 1.234000, // to become 1.234 y = 1.234001, // stays 1.234001 x_ = Number(x), y_ = Number(y); console.log(x_,y_);

After reading all of the answers - and comments - I ended up with this:

function isFloat(n) {
    let number = (Number(n) === n && n % 1 !== 0) ? eval(parseFloat(n)) : n;
    return number;
}

I know using eval can be harmful somehow but this helped me a lot.

So:

isFloat(1.234000);     // = 1.234;
isFloat(1.234001);     // = 1.234001
isFloat(1.2340010000); // = 1.234001

If you want to limit the decimal places, use toFixed() as others pointed out.

let number = (Number(n) === n && n % 1 !== 0) ? eval(parseFloat(n).toFixed(3)) : n;

That's it.

I needed to remove any trailing zeros but keep at least 2 decimals, including any zeros.
The numbers I'm working with are 6 decimal number strings, generated by .toFixed(6).

Expected Result:

var numstra = 12345.000010 // should return 12345.00001
var numstrb = 12345.100000 // should return 12345.10
var numstrc = 12345.000000 // should return 12345.00
var numstrd = 12345.123000 // should return 12345.123

Solution:

var numstr = 12345.100000

while (numstr[numstr.length-1] === "0") {           
    numstr = numstr.slice(0, -1)
    if (numstr[numstr.length-1] !== "0") {break;}
    if (numstr[numstr.length-3] === ".") {break;}
}

console.log(numstr) // 12345.10

Logic:

Run loop function if string last character is a zero.
Remove the last character and update the string variable.
If updated string last character is not a zero, end loop.
If updated string third to last character is a floating point, end loop.

My solution is as follow:

export const floatToStr = (f, ndigit = 2) => {
    const str = f.toFixed(ndigit)
    return ndigit
        ? str
            .replace(/0*$/g, '')
            .replace(/\.$/, '')
        : str
}

I wrote this regular expression to remove insignificant: zeros, decimals, and spaces from the beginning and end of strings containing numbers:

 const rxInsignificant = /^[\\s0]+|(?<=\\..*)[\\s0.]+$|\\.0+$|\\.$/gm; let ary = [ "001.230", "2.", "3.00", " 0000000000000010000.10000000000000000000000 "]; ary = ary.map((str)=> { str = str.replace(rxInsignificant,''); return str; }); console.log(ary);

Unfortunately, Safari still doesn't support the 2018 specification that gave us lookbehinds in regular expressions. There has been an open bug report for this issue since 07-28-2017.

The good news is that lookbehinds do work in Firefox and in all Chromium derivatives. Hopefully, Safari will receive more requests for this compliance and implement this standard soon.

If you'd also like to deal with numeric errors, you could use Intl.NumberFormat or Number.toLocaleString()

new Intl.NumberFormat().format(0.0100) // "0.01"
new Intl.NumberFormat().format(0.010000000000001) // "0.01"
new Intl.NumberFormat().format(0.009999999999999) // "0.01"
console.log((0.0100).toLocaleString()) // "0.01"
console.log((0.010000000000001).toLocaleString()) // "0.01"
console.log((0.009999999999999).toLocaleString()) // "0.01"

Using parseFloat() did the trick for me. I'm scratching my head here as to why those complicated solutions.

Here's a possible solution:

var x = 1.234000 // to become 1.234;
var y = 1.234001; // stays 1.234001

eval(x) --> 1.234
eval(y) --> 1.234001

Just a simple math alternative to using toFixed

 function truncateNumber( num, precision ){ let c = Math.pow(10,precision); return Math.trunc( num*c )/c; } console.log( truncateNumber(1234.5678, 4) ); console.log( truncateNumber(1234.56, 4) ); 

I think the following function may be close to what you want. I wrote it for an application of mine. It will always output in standard notation with no trailing zeros. A few things you may not want but can edit out if you like. It will always return at least one decimal(ex 5=>"5.0"). It also is limited to 10 decimals. Use it as a guide.

const toDecimalStr(value)=>{
  let str=value.toFixed(10).replace(/([0]+)$/,"");
  try {
    if (str.endsWith(".")) str+='0';
  } catch (e) {
    str+='0';
  }
  return str;
}

the try catch is because not everything supports endsWith and I was being lazy.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM