Javascript. Increase or decrease float as little as possible

若如初见. 提交于 2019-12-11 01:02:01

问题


In javascipt I have av floating point "a" like this:

var a = 5.;

Now I want a new number "b" that is barely larger than "a". I could do this:

var b = a + 1.e-10;

But what if "a" is a really small number?

var a = 5.e-20;
var b = a + 1.e-10;

Now "b" is many orders of magnitude larger than "a".
Also if I make the difference between "a" and "b" too small, a large "a" could cause the difference to be rounded off.

How do I make the number "b" larger than any number "a", but closer to "a" than any other number that is larger than "a", or how do I make a number "b" that is smaller than "a" but closer to "a" than any other number smaller than "a".

Edit:
Too be more specific: I'm looking for a function "makeLarger(a)" That takes a number "a" and return a number "b" where "b>a" will always evaluate to true and "c>a && c<b" will always evaluate to false for any number "c". And also a similar function "makeSmaller(a)". I want "a" to be any number, positive, negative or zero.


回答1:


Assuming a is positive, real and sufficiently far away from being a subnormal (in this case, greater than 1.0020841800044864e-292), then the following should work:

var u = Number.EPSILON/2 + Number.EPSILON*Number.EPSILON;
var b = a + a*u;

Note that b = a * (1+u) won't work. (e.g. if a = 0.9999999999999998).

The basic idea is that the gap between floating point numbers is roughly proportional, but only increases in steps (it is the same for all numbers in the same binade). So the challenge is to choose u small enough so that it works for the extremes in each binade.

So without loss of generality, it is sufficient to consider the numbers a in the interval [1.0,2.0). We need to ensure that

Machine.EPSILON/2 < a*u < Machine.EPSILON*3/2

so that the final addition will round in the correct direction (instead of back to a or 2 increments). It is fairly straightforward to show that the u defined above satisfies these properties.

To go downwards you can do

var c = a - a*u;

P.S.: Another option, though trickier to prove, is

var v = 1 - Machine.EPSILON/2;
var b = a / v; # upwards
var c = a * v; # downwards

This has the advantage of working for a greater range (any positive, non-subnormal real number).

For subnormals, you can just add/subtract Number.MIN_VALUE, so combining this all together you get:

function nextup(a) {
    var v = 1 - Number.EPSILON/2;
    if (a >= Number.MIN_VALUE / Number.EPSILON) {
        // positive normal
        return (a/v);
    } else if (a > -Number.MIN_VALUE / Number.EPSILON) {
        // subnormal or zero
        return (a+Number.MIN_VALUE);
    } else {
        // negative normal or NaN
        return (a*v);
    }
 }

 function nextdown(a) {
    var v = 1 - Number.EPSILON/2;
    if (a >= Number.MIN_VALUE / Number.EPSILON) {
        // positive normal
        return (a*v);
    } else if (a > -Number.MIN_VALUE / Number.EPSILON) {
        // subnormal or zero
        return (a-Number.MIN_VALUE);
    } else {
        // negative normal or NaN
        return (a/v);
    }
 }



回答2:


this may be unorthodox, but you can do the following:

  1. cast the float to a string, you need not be concerned with the size of the number then (within reason).
  2. find the index of the decimal point.
  3. retrieve a significant group of numbers from the end (tweaking required here - something like 'up to the first couple that aren't 0 led by a 9 or whatnot')
  4. cast them to an int and increase by 1
  5. replace the numbers in the first string with those in the second into var b
  6. place the decimal (at the previous index), cast to float.

there are some fine tuning aspects to work out but that will accomplish it.

I dont feel like writing the code for the example, but that is trivial.




回答3:


You might use a Big Number approach -- to store your number as a sequence of its digits: e. g. 123... many digits here ...45 as [1, 2, 3, .., 4, 5]

So you can handle reeeeeally loooooong numbers and have really great precision of your floats.

There are several js modules for this, e.g. http://jsfromhell.com/classes/bignumber



来源:https://stackoverflow.com/questions/48330463/javascript-increase-or-decrease-float-as-little-as-possible

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!