问题
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