How do I check if a float is an integer "within machine precision"?

A short version of my question:

What is considered "best practice" for deciding what a floating point number is x

and Math.round(x)

can be considered equal to reduce the precision of floating point operations?


Long term version:

I often need to decide whether a "floating point setpoint x

" should be considered an integer "or more pedantic," considered a floating point integer representation ".

(For example, if n is an integer, the mathematical expression

enter <sub> 10sub> (10 p )

is a collapsed way of representing the same integer n. This is the thinking that motivates to say that the result of a similar floating point computation can be viewed as "representing an integer".)

The solution is easy when Math.round(x) == x

evaluated as true

: in this case, we can say that there is x

indeed a (floating point representation) an integer.

But the test Math.round(x) == x

is inconclusive when it estimates value false

. For example,

function log10(x) { return Math.log(x)/Math.LN10; }
// -> function()
x = log10(Math.pow(10, -4))
// -> -3.999999999999999
Math.round(x) == x
// -> false

      

EDIT: One "solution" I see often is to choose an arbitrary tolerance, for example ε = 1e-6

, and test for Math.abs(Math.round(x) - x) < ε

. I think such solutions will lead to more false positives than I think are acceptable.

+3


source to share


3 answers


As you can see in your example x

, it is not really an integer. This is due to round-off errors earlier in your calculations, and thus you cannot really know if it was defined x

as a nearly round number or a round number that received jagged round-off errors.

If you want to know which numbers are one or the other, you will need to use the aproach limit you suggested to yourself, or use a priority high enough so that your numbers are not jagged in the first place. This latter approach is not applicable in all cases.



It is also possible to trace all mathematical operations symbolically, i.e. store 1/3

as 1/3

, not 0.3333

, and evaluate them on demand, overriding factors that can be undone, as if you were evaluating the expression manually, but that was completely overkill in almost all cases. Not to mention how complex such a system is. If this is the desired solution, you can perhaps interact with MatLab or Mathematica or something to handle the estimate, if you are not doing this in a browser where it might be slightly more difficult to do for browsers try the WolframAlpha API (why didn't I think about this the first time?).

However, if you can fix the problem by choosing ε

this way, you will get a satisfactory result, which is probably the best way to do it. If static ε

didn't cut it out, you can try to pick it dynamically based on what types of calculations were done previously per number. That is, the numbers that are multiplied tend to create a smaller fraction of the fraction than the divided numbers, and so on. In cases where a number has not been subject to anything other than plus, minus and multiplication (without the participation of fractions), you can find out how many decimal places it can have maximum, and thus can choose a reasonable one ε

.

+5


source


It's fun, so I had to think about it a bit.

Here's a one-line solution, although it involves converting the number and types of strings, so I don't know how optimal this is. But that would be much more accurate than just picking a minimum threshold and checking if the number fits within that limit.

JavaScript numbers are a double-precision 64-bit format that has about 16 decimal digits of precision. This is the sum of the digits, not just the number of digits to the right of the decimal point.

JavaScript numbers also have a toPrecision () method that converts them to strings rounded to the specified precision (generic numbers that are so good for your use). The following rounds any number to the nearest 15 digits of precision and then converts it back to float.

function roundToPrecision(number, precision) {
    return parseFloat(number.toPrecision(precision));
}

x = roundToPrecision(x, 15);

      



Then your example would be essentially an integer: -4.

Edit: After a little thought, this will be faster:

var integerDigits = (""+parseInt(Math.abs(x))).length,
    threshold = 1e-16 * Math.pow(10, integerDigits);

Math.abs(Math.round(x) - x) < threshold

      

http://jsperf.com/number-precision-rounding

+1


source


Assuming x

nonzero, I think you should be looking at the coefficient Math.abs(x-Math.round(x))/x

. This refers to the fact that floating point types store a fixed number of significant bits, not a fixed number of digits after the decimal point.

Then you need to determine the typical rounding error for your calculations. If it x

is the result of a simple calculation, it can be easy. If not, consider collecting some statistics from test cases for which you know the exact answer. Ideally, you will find that there is a significant difference between the largest ratio value for an x

integer and the smallest value for x

, which should not be treated as an integer. If so, select an epsilon in this range.

+1


source







All Articles