I have do some test about Number.prototype.toFixed method in chrome(v60.0.3112.101) console and found sth puzzled me.
Why 1.15.toFixed(1) return "1.1" but not the "1.2"?
Why 1.05.toFixed(1) return "1.1" but not the "1.0"?
and so on...
I do research in the ECMAScript specification.
NOTE 1
toFixed returns a String containing this Number value represented in decimal fixed-point notation with fractionDigits digits after the decimal point. If fractionDigits is undefined, 0 is assumed.
I know what's the fixed point notation.But I can't explain the puzzles above. Could someone give a clear explaination?
BTW, I think the details arithmetic under the specification should be improved.
Saying 1.105 for instance, the relative arithmetic is the following:
Let n be an integer for which the exact mathematical value of n รท 10^f - x is as close to zero as possible. If there are two such n, pick the larger n.
According to pick the larger n, 111 should be taken into consideration but not the 110, which is contradicted to reality.
I'll try my best to clarify the points around that question. First of all the fixed-point notation:
I know what's the fixed point notation. But I can't explain it well.
The fixed-point natation is opposed to the floating point notation. The floating point notation allow a better precision most of the time. But it also is more difficult to understand and to compute.
Well, let's go back to the fixed-point notation. This is also an arithmetic notation for real numbers. The difference is that a number in fixed-point notation is represented by an integer with a scaling factor.
For example :
If you want to write 4.56 and you got a 1/1000 scaling factor, your number will be represented by 4560. Indeed, 4560 * (1/1000) = 4.56
Now that we know how does the fixed-point notation does work, we can better understand the results of the toFixed(n) function. Let's say for the example that the scaling factor is 1/1000 (that is not the real value but this is easier to visualise the results).
1.15.toFixed(1)
Will take on decimal and then represent the number with a fixed-point notation, so it does not care about the '5'. The number you got in memory is 1100. This is why the number is rounded to the closest inferior value.
Now as you can see on the MDN Doc of toFixed function the you can keep up to 20 decimals. From that information we can say that the scaling factor is 1/10^20.
I hope that answers your questions.
Related
How to get big power of 2 in decimal or
how to convert big exponential value into decimal value.
I want 2 to the power of 128 in decimal not exponential
what I did till now
tofixed(+exponent)
which again given me the same value.
var num = Math.pow(2, 128);
Actual result = 3.402823669209385e+38
expected some decimal value not exponential value.
You could use BigInt, if implemented.
var num = BigInt(2) ** BigInt(128);
console.log(num.toString());
console.log(BigInt(2 ** 128).toString());
3.402823669209385e+38 is a decimal number (in string form, because it's been output as a string). It's in scientific notation, specifically E-notation. It's the number 3.402823669209385 times 100000000000000000000000000000000000000.
If you want a string that isn't in scientific notation, you can use Intl.NumberFormat for that:
console.log(new Intl.NumberFormat().format(Math.pow(2, 128)));
Note: Although that number is well outside the range that JavaScript's number type can represent with precision in general (any integer above Number.MAX_SAFE_INTEGER [9,007,199,254,740,991] may be the result of rounding), it's one of the values that is held precisely, even at that magnitude, because it's a power of 2. But operations on it that would have a true mathematical result that wasn't a power of 2 would almost certainly get rounded.
I think the default power function won't be able to the results you want.
You can refer to the article below to understand how to create an Power function with big number by yourself.
Demo code is not JS but still quite understandable.
โ
Writing power function for large numbers
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!
Let N(x) be the value of the decimal numeral with the fewest significant digits
such that x is the double value nearest the value of the numeral.
Given double values a and b, how can we compute the double value nearest N(b)-N(a)?
E.g.:
If a and b are the double values nearest .2 and .3,
the desired result is the double value nearest .1,
0.1000000000000000055511151231257827021181583404541015625,
rather than than the result of directly subtracting a and b,
0.09999999999999997779553950749686919152736663818359375.
As a baseline: In Java, the Double.toString() provides the N(x) function described in the question, returning its value as a numeral. One could take the strings for a and b, subtract them with the elementary-school method, and convert the resulting string to double.
This demonstrates solving the problem is quite feasible using existing library routines. This leaves the task of improving the solution. I suggest exploring:
Is there a function D(x) that returns the number of significant digits after the decimal place for the numeral described in N(x)? If so, can we multiply a and b by a power of ten determined by D(a) and D(b), round as necessary to produce the correct integer results (for situations where they are representable as double values), subtract them, and divide by the power of ten?
Can we establish criteria for which b-a or some simple expression can be quickly rounded to something near a decimal numeral, bypassing the code that would be necessary for harder cases? E.g., could we prove that for numbers within a certain range, (round(10000*b)-round(10000*a))/10000 always produces the desired result?
You can convert to 'integers' by multiplying then dividing by a power of ten:
(10*.3 - 10*.2)/10 == 0.1000000000000000055511151231257827021181583404541015625
It may be possible to work out the appropriate power of ten from the string representation of the number. #PatriciaShanahan suggests looking for repeated 0's or 9's.
Consider using a BigDecimal library such as javascript-bignum instead.
You could also inquire in Smalltalk Pharo 2.0 where your request translates:
^(b asMinimalDecimalFraction - a asMinimalDecimalFraction) asFloat
Code could be found as attachment to issue 4957 at code.google.com/p/pharo/issues - alas, dead link, and the new bugtracker requires a login...
https://pharo.fogbugz.com/f/cases/5000/Let-asScaledDecimal-use-the-right-number-of-decimals
source code is also on github, currently:
https://github.com/pharo-project/pharo-core/blob/6.0/Kernel.package/Float.class/instance/printing/asMinimalDecimalFraction.st
The algorithm is based on:
Robert G. Burger and R. Kent Dybvig
Printing Floating Point Numbers Quickly and Accurately
ACM SIGPLAN 1996 Conference on Programming Language Design and Implementation
June 1996.
http://www.cs.indiana.edu/~dyb/pubs/FP-Printing-PLDI96.pdf
I've been playing around with floating point numbers a little bit, and based on what I've learned about them in the past, the fact that 0.1 + 0.2 ends up being something like 0.30000000000000004 doesn't surprise me.
What does surprise me, however, is that integer arithmetic always seems to work just fine and not have any of these artifacts.
I first noticed this in JavaScript (Chrome V8 in node.js):
0.1 + 0.2 == 0.3 // false, NOT surprising
123456789012 + 18 == 123456789030 // true
22334455667788 + 998877665544 == 23333333333332 // true
1048576 / 1024 == 1024 // true
C++ (gcc on Mac OS X) seems to have the same properties.
The net result seems to be that integer numbers just โ for lack of a better word โ work. It's only when I start using decimal numbers that things get wonky.
Is this is a feature of the design, an mathematical artifact, or some optimisation done by compilers and runtime environments?
Is this is a feature of the design, an mathematical artifact, or some optimisation done by compilers and runtime environments?
It's a feature of the real numbers. A theorem from modern algebra (modern algebra, not high school algebra; math majors take a class in modern algebra after their basic calculus and linear algebra classes) says that for some positive integer b, any positive real number r can be expressed as r = a * bp, where a is in [1,b) and p is some integer. For example, 102410 = 1.02410*103. It is this theorem that justifies our use of scientific notation.
That number a can be classified as terminal (e.g. 1.0), repeating (1/3=0.333...), or non-repeating (the representation of pi). There's a minor issue here with terminal numbers. Any terminal number can be also be represented as a repeating number. For example, 0.999... and 1 are the same number. This ambiguity in representation can be resolved by specifying that numbers that can be represented as terminal numbers are represented as such.
What you have discovered is a consequence of the fact that all integers have a terminal representation in any base.
There is an issue here with how the reals are represented in a computer. Just as int and long long int don't represent all of integers, float and double don't represent all of the reals. The scheme used on most computer to represent a real number r is to represent in the form r = a*2p, but with the mantissa (or significand) a truncated to a certain number of bits and the exponent p limited to some finite number. What this means is that some integers cannot be represented exactly. For example, even though a googol (10100) is an integer, it's floating point representation is not exact. The base 2 representation of a googol is a 333 bit number. This 333 bit mantissa is truncated to 52+1 bits.
On consequence of this is that double precision arithmetic is no longer exact, even for integers if the integers in question are greater than 253. Try your experiment using the type unsigned long long int on values between 253 and 264. You'll find that double precision arithmetic is no longer exact for these large integers.
I'm writing that under assumption that Javascript uses double-precision floating-point representation for all numbers.
Some numbers have an exact representation in the floating-point format, in particular, all integers such that |x| < 2^53. Some numbers don't, in particular, fractions such as 0.1 or 0.2 which become infinite fractions in binary representation.
If all operands and the result of an operation have an exact representation, then it would be safe to compare the result using ==.
Related questions:
What number in binary can only be represented as an approximation?
Why can't decimal numbers be represented exactly in binary?
Integers withing the representable range are exactly representable by the machine, floats are not (well, most of them).
If by "basic integer math" you understand "feature", then yes, you can assume correctly implementing arithmetic is a feature.
The reason is, that you can represent every whole number (1, 2, 3, ...) exactly in binary format (0001, 0010, 0011, ...)
That is why integers are always correct because 0011 - 0001 is always 0010.
The problem with floating point numbers is, that the part after the point cannot be exactly converted to binary.
All of the cases that you say "work" are ones where the numbers you have given can be represented exactly in the floating point format. You'll find that adding 0.25 and 0.5 and 0.125 works exactly too because they can also be represented exactly in a binary floating point number.
it's only values that can't be such as 0.1 where you'll get what appear to be inexact results.
Integers are exact because because the imprecision results mainly from the way we write decimal fractions, and secondarily because many rational numbers simply don't have non-repeating representations in any given base.
See: https://stackoverflow.com/a/9650037/140740 for the full explanation.
That method only works, when you are adding a small enough integer to very large integer -- and even in that case you are not representing both of the integers in the 'floating point' format.
All floating point numbers can't be represented. it's due to the way of coding them. The wiki page explain it better than me: http://en.wikipedia.org/wiki/IEEE_754-1985.
So when you are trying to compare a floating point number, you should use a delta:
myFloat - expectedFloat < delta
You can use the smallest representable floating point number as delta.
I have noticed some inconsistencies between Python and JavaScript when converting a string to base36.
Python Method:
>>> print int('abcdefghijr', 36)
Result: 37713647386641447
Javascript Method:
<script>
document.write(parseInt("abcdefghijr", 36));
</script>
Result: 37713647386641450
What causes the different results between the two languages? What would be the best approach to produce the same results irregardless of the language?
Thank you.
That number takes 56 bits to represent. JavaScript's numbers are actually double-precision binary floating point numbers, or double for short. These are 64 bit in total, and can represent a far wider range of values than a 64 bit integers, but due to how they achieve that (they represent a number as mantissa * 2^exponent), they cannot represent all numbers in that range, just the ones that are a multiple of 2^exponent where the multiple fits into the mantissa (which includes 2^0 = 1, so you get all integers the mantissa can handle directly). The mantissa is 53 bits, which is insufficient for this number. So it gets rounded to a number which can be represented.
What you can do is use an arbitrary precision number type defined by a third party library like gwt-math or Big.js. These numbers aren't hard to implement if you know your school arithmetic. Doing it efficiently is another matter, but also an area of extensive research. And not your problem if you use an existing library.