Skip to content

Commit

Permalink
fix(numberFilter): format large numbers correctly
Browse files Browse the repository at this point in the history
format large number to correct form instead of NAN.00 currently.
in previous code when first time we add the exponent with fractionSize
(number.toString()+'e'+fractionsize)
 and then convert it to an integer using ,
+(number.toString()+'e'+fractionsize)
when the number is very large and number will be represented by using exponent. for example 12345868059685210000 will be represented by 1.234586805968521e+21. as getting string in exponent is not handles, we get the number as NAN.
Handle when the number is being represented in exponent form, by adjusting exponent of number with fraction size:

Closes angular#8674
  • Loading branch information
gauravaror committed Aug 23, 2014
1 parent 8863b9d commit 1567e58
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 1 deletion.
13 changes: 12 additions & 1 deletion src/ng/filter/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,17 @@ function numberFilter($locale) {
};
}

function shiftDecimalPlace(number,fractionSize) {
var numberArray = number.toString().split('e');
var fractionUsed = +fractionSize;
//If number was already an exponent, adjust the exponent value rather than adding new exponent.
if(numberArray[1]) {
fractionUsed = +numberArray[1] + fractionUsed;
}
//shift the number by fractionSize and return
return +(numberArray[0] + 'e' + fractionUsed);
}

var DECIMAL_SEP = '.';
function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
if (number == null || !isFinite(number) || isObject(number)) return '';
Expand Down Expand Up @@ -151,7 +162,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
// safely round numbers in JS without hitting imprecisions of floating-point arithmetics
// inspired by:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
number = shiftDecimalPlace(Math.round(shiftDecimalPlace(number,fractionSize)),-fractionSize);

var fraction = ('' + number).split(DECIMAL_SEP);
var whole = fraction[0];
Expand Down
12 changes: 12 additions & 0 deletions test/ng/filter/filtersSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ describe('filters', function() {
expect(num).toBe('1.1112');
});

it('should format large number',function() {
pattern.gsize = 2;
var num = formatNumber(12345868059685210000, pattern, ',', '.', 2);
expect(num).toBe('12,345,868,059,685,210,000.00');
var num = formatNumber(79832749837498327498274983793234322432, pattern, ',', '.', 2);
expect(num).toBe('7.983274983749832e+37');
var num = formatNumber(8798327498374983274928, pattern, ',', '.', 2);
expect(num).toBe('8.798327498374983e+21');
var num = formatNumber(879832749374983274928, pattern, ',', '.', 2);
expect(num).toBe('879,832,749,374,983,200,000.00');

This comment has been minimized.

Copy link
@gauravaror

gauravaror Aug 23, 2014

Author Owner

I was trying to write some additional test cases is when i found out that .. When we apply the number filter to 879832749374983274928 we get result 879,832,749,374,983,200,000.00

Which is not a correct result since all the digits after 16 digits is being converted to zero, which is wrong.

This is happening because
They are 64-bit floating point values, the largest exact integral value is 253, or 9007199254740992
any number greater than this number will get the zeros after 16 digits and there by loose precision at the least.
this loose of precision is outside of the scope of number filter

This comment has been minimized.

Copy link
@gauravaror

gauravaror Aug 23, 2014

Author Owner

There is one more test case i found, but this doesn't look like a valid case also:
8798327493749832749 | number:3342
If this is the filter.

Output returned will be NAN.000000000000000000000000000000000000000

Problem comes when we, try to shift the number by the facction size . fractionSize here is very high, so shifting yields a infinity. which goes and converts into NAN on further processing.

});

it('should format according different separators', function() {
var num = formatNumber(1234567.1, pattern, '.', ',', 2);
expect(num).toBe('1.234.567,10');
Expand Down

1 comment on commit 1567e58

@gauravaror
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done with the other suggested changes, but since number is roundedoff after 16 digit, i am thinking will this change be useful.

Please sign in to comment.