convert float to short with minimal loss of precision
Solution 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);
Solution 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.
Solution 3
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;
Solution 4
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.
user3840530
Updated on June 28, 2022Comments
-
user3840530 almost 2 years
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?
-
barak manos over 9 yearsWhy is that an issue? If you take an integer of the same size (
int
would do on most platforms) then there's no problem with regards to the type of data. For example, givenfloat f
andint i
, you can doi = *(int*)&f
. -
BackSlash over 9 years@barakmanos What is that
&
? -
T.J. Crowder over 9 years@barakmanos: This is Java, not C. You could probably do something similar to that in Java as well, but A)
float
andshort
are not the same size, and B) It's extremely unlikely that the OP wants theshort
(orint
) view of the bits that made up thefloat
. :-) -
barak manos over 9 yearsOoops, sorry (@BackSlash - same apology to you) ... :)
-
Joop Eggen over 9 years@barakmanos funny, you know that just stores the bits, but the mathematical value then is totally different. Java has Float.floatToRawIntBits
-
barak manos over 9 years@JoopEggen: 1. In my answer I was referring to C/C++ (my mistake). 2. If you consider storing that value for later use, and for some reason you want to store it in an integer-type value, then you can simply "store the bits" in an
int
. Once you read them back into afloat
, it will retain its original (floating-point) value. -
user3840530 over 9 yearsI agree that there will be loss of precision but all I wanted was a workaround. I will mark this as the answer( if it allows me to).
-
user3840530 over 9 yearsThanks for the Save the bits method. That will be helpful.
-
Rudy Velthuis over 9 yearsI think that is the only correct answer, but assuming his sine wave returns values between -1 and 1 (inclusive), I would multiply them by 65535 and then subtract 32767.
-
Stéphane about 4 yearsThis won't work. Ex for the value of 1: 1 * 65535 - 32767 = 32768. This is wrong, it is outside of short range. Other example: -1 * 65535 - 32767 will give -98302.