convert float to short with minimal loss of precision [closed]

不问归期 提交于 2020-01-02 05:46:44

问题


I have this sine wave which generates floating point values (e.g. 0.37885) but I want them as shorts. Direct casting with short gives me a value of 0. so what is the solution?

Can anyone tell me how to do it - ideally without loss of precision - or minimal loss of precision if this is all that is possible?


回答1:


public static short floatToShort(float x) {
    if (x < Short.MIN_VALUE) {
        return Short.MIN_VALUE;
    }
    if (x > Short.MAX_VALUE) {
        return Short.MAX_VALUE;
    }
    return (short) Math.round(x);
}

You'll loose the fractional part:

float    4 byte floating-point
double   8 byte floating-point (normal)
short    2 byte integer
int      4 byte integer (normal)
long     8 byte integer

Edit:

Maybe you wanted to know how to save the bits of a float (4 bytes) into an int (4 bytes): (http://docs.oracle.com/javase/7/docs/api/java/lang/Float.html#floatToRawIntBits(float))

float x = 0.1f;
int n = Float.floatToRawIntBits(x);
float y = Float.intBitsToFloat(n);



回答2:


In principle, you could just multiply it by 100000, convert it to int, then subtract -32,767 and convert it to short. If that still puts it in the -32,767 to 32,767 range for all your values, that's likely the best you can do. Otherwise, you'll have to limit your precision and multiply by 10000.

And when you use the short of course you have to remember to divide it back down.




回答3:


short is an integral type, so it can only contain whole numbers. The only two choices for 0.37885 in a short are 0 or 1, both of which (it seems to me) lose quite a bit of precision.

So the answer is: If you're okay with losing all fractional values, either use a cast, Float#shortValue, or Math.round(float) (and cast the resulting int to short).

Example: Live Copy

float f1 = 0.37885f;
short s1 = (short)Math.round(f1);
System.out.println("s1 = " + s1);

float f2 = 27.67885f;
short s2 = (short)Math.round(f2);
System.out.println("s2 = " + s2);

Output:

s1 = 0
s2 = 28

In a comment you said:

I have this sine wave which generates values like the one mentioned above, but I want them as shorts.

Ah, now, we can do something with that. Presumably the values you're getting are all between 0 and 1. You can store them as shorts by multiplying. Since the range of a short is -32,768 to 37,767, a convenient number to multiply them by might be 10000:

short s = Math.round(floatValue * 10000);

The number we'd get for your example would be 3789. Example: Live Copy

float floatValue = 0.37885f;
short s = (short)Math.round((double)floatValue * 10000);
System.out.println("s = " + s);

That isn't the same value, of course, it's the value multipled by ten thousand, so anywhere you're going to use it, you'd have to allow for that.




回答4:


If your input float values are in a defined range (for now let's assume they're in the range of -1..1, exclusive), you can multiply them to get a value whose fraction you'll throw away.

Valid short range is: -32768..32767 so you can multiple with 32768 in this case (max short / max input value).

For example:

float f = 0.23451f;
short s = (short) (f * 32768);

To decode a short value to float:

float f2 = s / 32768f;


来源:https://stackoverflow.com/questions/25201304/convert-float-to-short-with-minimal-loss-of-precision

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