Force decimal value, even on integers, in Javascript - javascript

I need to take a user-input value and force it to 6 decimal places, even if the value is an integer. For example, the user types in 12, I need to convert that to 12.000000, as a number. This is not for display purposes - the system on the other end of my app requires decimal values, and there's nothing I can do about that.
As I've read elsewhere, numbers in Javascript are all 64-bit floating point numbers, so it doesn't seem like this should be so difficult.
Alas, toFixed is not an option here because that gives me a string value '12.000000'. Every other trick I've tried just yields the integer 12 with no decimal zeroes (e.g. wrapping toFixed with Number, dividing the string value by 1, and other such silliness).
Is it possible to represent an integer as a float in Javascript, without ending up with a string value?
UPDATE
Thanks for all the comments and answers. Unfortunately for me, #Enzey's comment actually answers my core question when he said that forcing precision can only be done with a string. If he submits that as an answer I'll accept it. I kept the details of my implementation purposefully vague because I didn't want to get into why I wanted to do what I'm doing, I just wanted to know if it was possible. But I guess I just ended up confusing people. Sorry about that.

Alas, there is no such thing as float or int in JavaScript. You only have Number, which does not have the slightest clue about a difference between 12 and 12.000000.

If you're sending it as a stringified JSON, you can use .toFixed on the number, and then strip the " signs from the numbers in the stringified JSON:
var result = JSON.stringify({
number: (12).toFixed(6)
})
.replace(/"[\d]+\.\d{6}"/g, function(v) {
return v.replace(/"/g, '');
});
console.log(result);

Related

JSON.stringify() converts integers to exponential

I have a record
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
1000000000000000000000
],
JSON.stringify() converts it to the form
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
1e+21
],
JSON.stringify() writes it accordingly the same way, can this be somehow solved?
JSON.parse doesn't convert it to 1e+21, it converts it to a number that, when converted to string in the usual way, is output as the string "1e+21". But the number is the same number whether you write it as 1000000000000000000000 or 1e+21.
JSON.stringify may output it in either form; both are valid JSON numbers, and both define exactly the same number.
I should note that you need to beware of numbers of that magnitude in JavaScript (or any other language that uses IEEE-754 double-precision floating point numbers [or single-precision ones, actually]). That number is well into the range where even integers may be imprecisely represented. Any number greater than 9,007,199,254,740,992 (Number.MAX_SAFE_INTEGER + 1) may or may not have a precise representation. It happens that 10,000,00,000,000,000,000,000 (your number) does, but for instance, 9,007,199,254,740,993 doesn't, nor do any odd numbers from that point upward. At some point, you get to where only multiples of 4 can be represented; and then later it's only multiple of 8, etc. See this question's answers for more.
If you still need to get 1e+21 as 1000000000000000000000, you can use (1e+21).toLocaleString().split(',').join('')
but actually, you don't need to convert it if you want to use it as a number, because they are absolutely the same.
Instead, you can keep the number as string and use +'1000000000000000000000' or parseInt('1000000000000000000000') when you need to use it as a number.

Rounding up or down when 0.5

I am having an issue with the way Javascript is rounding numbers when hitting 0.5.
I am writing levies calculators, and am noticing a 0.1c discrepancy in the results.
The problem is that the result for them is 21480.705 which my application translates into 21480.71, whereas the tariff says 21480.70.
This is what I am seeing with Javascript:
(21480.105).toFixed(2)
"21480.10"
(21480.205).toFixed(2)
"21480.21"
(21480.305).toFixed(2)
"21480.31"
(21480.405).toFixed(2)
"21480.40"
(21480.505).toFixed(2)
"21480.51"
(21480.605).toFixed(2)
"21480.60"
(21480.705).toFixed(2)
"21480.71"
(21480.805).toFixed(2)
"21480.81"
(21480.905).toFixed(2)
"21480.90"
Questions:
What the hell is going on with this erratic rouding?
What's the quickest easiest way to get a "rounded up" result (when hitting 0.5)?
So as some of the others already explained the reason for the 'erratic' rounding is a floating point precision problem. You can investigate this by using the toExponential() method of a JavaScript number.
(21480.905).toExponential(20)
#>"2.14809049999999988358e+4"
(21480.805).toExponential(20)
#>"2.14808050000000002910e+4"
As you can see here 21480.905, gets a double representation that is slightly smaller than 21480.905, while 21480.805 gets a double representation slightly larger than the original value. Since the toFixed() method works with the double representation and has no idea of your original intended value, it does all it can and should do with the information it has.
One way to work around this, is to shift the decimal point to the number of decimals you require by multiplication, then use the standard Math.round(), then shift the decimal point back again, either by division or multiplication by the inverse. Then finally we call toFixed() method to make sure the output value gets correctly zero-padded.
var x1 = 21480.905;
var x2 = -21480.705;
function round_up(x,nd)
{
var rup=Math.pow(10,nd);
var rdwn=Math.pow(10,-nd); // Or you can just use 1/rup
return (Math.round(x*rup)*rdwn).toFixed(nd)
}
function round_down(x,nd)
{
var rup=Math.pow(10,nd);
var rdwn=Math.pow(10,-nd);
return (Math.round(x*-rup)*-rdwn).toFixed(nd)
}
function round_tozero(x,nd)
{
return x>0?round_down(x,nd):round_up(x,nd)
}
console.log(x1,'up',round_up(x1,2));
console.log(x1,'down',round_down(x1,2));
console.log(x1,'to0',round_tozero(x1,2));
console.log(x2,'up',round_up(x2,2));
console.log(x2,'down',round_down(x2,2));
console.log(x2,'to0',round_tozero(x2,2));
Finally:
Encountering a problem like this is usually a good time to sit down and have a long think about wether you are actually using the correct data type for your problem. Since floating point errors can accumulate with iterative calculation, and since people are sometimes strangely sensitive with regards to money magically disappearing/appearing in the CPU, maybe you would be better off keeping monetary counters in integer 'cents' (or some other well thought out structure) rather than floating point 'dollar'.
The why -
You may have heard that in some languages, such as JavaScript, numbers with a fractional part are calling floating-point numbers, and floating-point numbers are about dealing with approximations of numeric operations. Not exact calculations, approximations. Because how exactly would you expect to compute and store 1/3 or square root of 2, with exact calculations?
If you had not, then now you've heard of it.
That means that when you type in the number literal 21480.105, the actual value that ends up stored in computer memory is not actually 21480.105, but an approximation of it. The value closest to 21480.105 that can be represented as a floating-point number.
And since this value is not exactly 21480.105, that means it is either slightly more than that, or slightly less than that. More will be rounded up, and less will be rounded down, as expected.
The solution -
Your problem comes from approximations, that it seems you cannot afford. The solution is to work with exact numbers, not approximate.
Use whole numbers. Those are exact. Add in a fractional dot when you convert your numbers to string.
This works in most cases. (See note below.)
The rounding problem can be avoided by using numbers represented in
exponential notation:
function round(value, decimals) {
return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
}
console.log(round(21480.105, 2).toFixed(2));
Found at http://www.jacklmoore.com/notes/rounding-in-javascript/
NOTE: As pointed out by Mark Dickinson, this is not a general solution because it returns NaN in certain cases, such as round(0.0000001, 2) and with large inputs.
Edits to make this more robust are welcome.
You could round to an Integer, then shift in a comma while displaying:
function round(n, digits = 2) {
// rounding to an integer is accurate in more cases, shift left by "digits" to get the number of digits behind the comma
const str = "" + Math.round(n * 10 ** digits);
return str
.padStart(digits + 1, "0") // ensure there are enough digits, 0 -> 000 -> 0.00
.slice(0, -digits) + "." + str.slice(-digits); // add a comma at "digits" counted from the end
}
What the hell is going on with this erratic rouding?
Please reference the cautionary Mozilla Doc, which identifies the cause for these discrepancies. "Floating point numbers cannot represent all decimals precisely in binary which can lead to unexpected results..."
Also, please reference Is floating point math broken? (Thank you Robby Cornelissen for the reference)
What's the quickest easiest way to get a "rounded up" result (when hitting 0.5)?
Use a JS library like accounting.js to round, format, and present currency.
For example...
function roundToNearestCent(rawValue) {
return accounting.toFixed(rawValue, 2);
}
const roundedValue = roundToNearestCent(21480.105);
console.log(roundedValue);
<script src="https://combinatronics.com/openexchangerates/accounting.js/master/accounting.js"></script>
Also, consider checking out BigDecimal in JavaScript.
Hope that helps!

Add trailing zeros to an integer without converting to string in JS?

I'm looking to add decimals to the end of my integer. As an example:
15 => 15.00
The problem with methods like toFixed is that it will convert it into a string. I've tried to use parseFloat() and Number() on the string, but it'll convert it back to an integer with no decimals.
Is this possible? If not, can someone explain to me the logic behind why this isn't possible?
EDIT: Welp the intent was to display the number as a number, but from the going consensus, it looks like the way the only way to go about it is to use a string. Found an answer on the why: https://stackoverflow.com/a/17811916/8869701
The problem you are finding is that all numbers in javascript are floats.
a = 0.1
typeof a # "number"
b = 1
typeof b # number
They are the same.
So there is no real way to convert to from an integer to a float.
This is the reason that all of the parseFloat etc are string methods for reading and writing numbers from strings. Even if you did have floats and integers, specifying the precision of a number only really makes sense when you are displaying it to a user, and for this purpose it will be converted to a string anyway.
Depending on your exact use case you will need to use strings if you want to display with a defined precision.
When working with numbers 15 and 15.00 are equal. It wouldn't make any sense to use memory to store those trailing or leading zeros.
If that information is needed it is usually for displaying purposes. In that case a string is the right choice.
In case you need that value again you can parse the string as a number.

parseInt changes the integer

I am trying to pull a number (72157648141531978), which starts at the 21st character, out of the title of a page like so:
parseInt(document.title.substring(21), 10);
This returns the string as an integer of 72157648141531980. I can't seem to figure out why it is changing the last two numbers. Any help would be appreciated.
According to What is JavaScript's highest integer value that a Number can go to without losing precision? the max value of an integer is 9007199254740992.
I tried your calculation on http://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_parseint and I can confirm your problem.
It looks like an issue parsing beyond this max value and it is rounding the last 2 figures.
You have exceeded the limits of double-precision floating-point format, as used by JavaScript. You cannot use that precise number directly in JavaScript. You can use it as a string, but if you need to do arithmetic on it you will need a bignum library.

Javascript can't render this number correctly: 3494793310847464221

I have an interesting question, I have been doing some work with javascript and a database ID came out as "3494793310847464221", now this is being entered into javascript as a number yet it is using the number as a different value, both when output to an alert and when being passed to another javascript function.
Here is some example code to show the error to its fullest.
<html><head><script language="javascript">alert( 3494793310847464221);
var rar = 3494793310847464221;
alert(rar);
</script></head></html>
This has completly baffeled me and for once google is not my friend...
btw the number is 179 more then the number there...
Your number is larger than the maximum allowed integer value in javascript (2^53). This has previously been covered by What is JavaScript's highest integer value that a Number can go to without losing precision?
In JavaScript, all numbers (even integral ones) are stored as IEEE-754 floating-point numbers. However, FPs have limited "precision" (see the Wikipedia article for more info), so your number isn't able to be represented exactly.
You will need to either store your number as a string or use some other "bignum" approach (unfortunately, I don't know of any JS bignum libraries off the top of my head).
Edit: After doing a little digging, it doesn't seem as if there's been a lot of work done in the way of JavaScript bignum libraries. In fact, the only bignum implementation of any kind that I was able to find is Edward Martin's JavaScript High Precision Calculator.
Use a string instead.
179 more is one way to look at it. Another way is, after the first 16 digits, any further digit is 0. I don't know the details, but it looks like your variable only stores up to 16 digits.
That number exceeds (2^31)-1, and that's the problem; javascript uses 32-bit signed integers (meaning, a range from –2,147,483,648 to 2,147,483,647). Your best choice is to use strings, and create functions to manipulate the strings as numbers.
I wouldn't be all too surprised, if there already was a library that does what you need.
One possible solution is to use a BigInt library such as: http://www.leemon.com/crypto/BigInt.html
This will allow you to store integers of arbitrary precision, but it will not be as fast as standard arithmetic.
Since it's to big to be stored as int, it's converted to float. In JavaScript ther is no explicit integer and float types, there's only universal Number type.
"Can't increment and decrement a string easily..."
Really?
function incr_num(x) {
var lastdigit=Number(x.charAt(x.length-1));
if (lastdigit!=9) return (x.substring(0,x.length-1))+""+(lastdigit+1);
if (x=="9") return "10";
return incr_num(x.substring(0,x.length-1))+"0";
}
function decr_num(x) {
if(x=="0") return "(error: cannot decrement zero)";
var lastdigit=Number(x.charAt(x.length-1));
if (lastdigit!=0) return (x.substring(0,x.length-1))+""+(lastdigit-1);
if (x=="10") return "9"; // delete this line if you like leading zero
return decr_num(x.substring(0,x.length-1))+"9";
}
Just guessing, but perhaps the number is stored as a floating type, and the difference might be because of some rounding error. If that is the case it might work correctly if you use another interpreter (browser, or whatever you are running it in)

Categories