Get the next smallest nearest decimal

Introduction

For some calculations, I need to find the smallest possible number that I can add / subtract from the specified number without JavaScript errors related to the internal data type used.

goal

I tried to write a function that could return the next nearest number to VALUE in the direction of the DIR value.

function nextNearest(value, direction) { // Special cases for value==0 or value==direction removed if (direction < value) { return value - Number.MIN_VALUE; } else { return value + Number.MIN_VALUE; } } 

The problem is that JavaScript uses the 64-bit float type (I think), which has different minimum step sizes depending on its current metric.

Problem more

The problem is the step size depending on its current indicator:

 var a = Number.MIN_VALUE; console.log(a); // 5e-324 console.log(a + Number.MIN_VALUE); // 1e-323 (changed, as expected) var a = Number.MAX_VALUE; console.log(a); // 1.7976931348623157e+308 console.log(a - Number.MIN_VALUE); // 1.7976931348623157e+308 (that wrong) console.log(a - Number.MIN_VALUE == a); // true (which also is wrong) 

Summary

So, how can I find the smallest possible number that I can add / subtract from the value specified in the parameter in any direction? In C ++, this would be easy to get by looking at the binary values โ€‹โ€‹of numbers.

+6
source share
2 answers

I tried to implement Pointy's suggestion from comments (using typed arrays). It is freely adapted from the glibc nextafter implementation. It should be good enough.

You can simply increase / decrease the 64-bit integer double representation to get the desired result. The mantissa overflow will overflow with the exponent, which, as it turns out, will be what you want.

Since JavaScript does not provide Uint64Array , I had to implement manual overflow on two 32-bit integers.

This works on low-rise architectures, but I left because of the large number, since I have no way to test it. If you need this to work with the architecture of the big end, you will have to adapt this code.

 // Return the next representable double from value towards direction function nextNearest(value, direction) { if (typeof value != "number" || typeof direction != "number") return NaN; if (isNaN(value) || isNaN(direction)) return NaN; if (!isFinite(value)) return value; if (value === direction) return value; var buffer = new ArrayBuffer(8); var f64 = new Float64Array(buffer); var u32 = new Uint32Array(buffer); f64[0] = value; if (value === 0) { u32[0] = 1; u32[1] = direction < 0 ? 1 << 31 : 0; } else if ((value > 0) && (value < direction) || (value < 0) && (value > direction)) { if (u32[0]++ === 0xFFFFFFFF) u32[1]++; } else { if (u32[0]-- === 0) u32[1]--; } return f64[0]; } var testCases = [0, 1, -1, 0.1, -1, 10, 42e42, 0.9999999999999999, 1.0000000000000002, 10.00000762939453, // overflows between dwords 5e-324, -5e-324, // minimum subnormals (around zero) Number.MAX_VALUE, -Number.MAX_VALUE, Infinity, -Infinity, NaN]; document.write("<table><tr><th>n</th><th>next</th><th>prev</th></tr>"); testCases.forEach(function(n) { var next = nextNearest(n, Infinity); var prev = nextNearest(n, -Infinity); document.write("<tr><td>" + n + "</td><td>" + next + "</td><td>" + prev + "</td></tr>"); }); document.write("</table>"); 
+5
source

Number.MIN_VALUE is the smallest possible representable number, not the smallest possible difference between the represented numbers. Due to the way javascript handles floating point numbers, the smallest possible difference between the displayed numbers changes with the size of the number. As the number increases, accuracy becomes less. Thus, there is not a single number that will solve your problem. I suggest you either rethink how you are going to solve your problem, or choose a subset of numbers to use, rather than the full range of MAX and MIN values.

for example: 1.7976931348623156e+308 == 1.7976931348623155e+308 //true

Also, by subtracting MIN_VALUE from MAX_VALUE, you are trying to get javascript to represent the exact number with more than 600 significant digits. It's too much.

0
source

Source: https://habr.com/ru/post/980180/


All Articles