3
\$\begingroup\$

Bit of a math question here...

lets say I want to supply a number (any number) and I want the result to be clamped between 0 and 1.

For instance, see the following graph.

enter image description here

Is there a way that when a number larger than 1 is supplied, that it returns a number that is still within those bounds (0 - 1), but still resulting in a number larger than any smaller supplied numbers that came before it (for instance, a supplied 2 would be a slightly larger number than a supplied 1.5)?

Thanks for your help!

\$\endgroup\$
2
\$\begingroup\$

How about this?

graph

You can increase the exponent to make it "steeper." The expression "1/(x+1)" is bounded between (0,1] for x >=0, so subtracting any exponent of that will never quite reach 1.

\$\endgroup\$
1
\$\begingroup\$

This is a tricky problem to solve in practice.

The answer is yes and no.


No:

Not unless you use a value format with better precision to contain your result value (0 ... 1) than the value format of your original value.

In your question you want to ensure that a number on the input side of your function that is larger than another number on the input side will always give a larger number on the output side.

Due to precision loss you cannot guarantee this unless you use a larger format.

Otherwise very close but distinct numbers on the function's input will give the same result on the output. This is probably something you do not want.

(A != B) && (Remap(A) == Remap(B))

Is guaranteed to be true for some cases if you do not use a larger precision for the result of your Remap function.


Yes:

There are multiple ways to do this but all require a larger precision for the result.

One very simple and quick way for positive numbers if your language and compiler uses IEEE 754 (Also known as IEC 60559) floating points and allows re-interpretation of bits in memory. Is to interpret the bits of this floating point positive number as an integer of the same bit size, convert it to a larger floating point with enough bits to contain the integer value, then divide the result by (2 to the power of integer_bits-1).

C example:

#include <stdint.h>
double RemapPositives(float x){
    return (*(int32_t *)&x) * (1.0 / 2147483648.0);
}

This works because the standard guarantees that comparing two positive floating points stored in memory as integers will give the same relation (larger, equal, smaller).

But in your question you say:

I want to supply a number (any number)

So to do this with negative floating point numbers you must convert the 1's complement value.

#include <stdint.h>
double Remap(float x){
    uint32_t v = *(uint32_t *)&x;
    if(v & ((uint32_t)1 << 31)){
        v = ~v;
    } else {
        v = v + ((uint32_t)1 << 31);
    }
    return v * (1.0 / 4294967296.0);
}

If you need to do this with double (64bits) floating points as input you must be able to use a larger floating point format (80bits or 128bits). Not all platforms support floating points larger than 64bits.

\$\endgroup\$
  • \$\begingroup\$ Upvote for the precision issue. \$\endgroup\$ – Chris Jun 15 '17 at 15:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.