I need to convert floats to unsigned integers for a project I'm working on — similar to the basic float-to-int cast, but with an unsigned int as output, returning zero for floats that are negative. I know there's a way to convert them to signed integers, but I need unsigned integers. I can't simply upscale to longs, since I'll also need to convert doubles to unsigned longs. The only way I've found is to convert to a string, then to an unsigned int:
private static NumberFormat genIntFormat() {
NumberFormat numberFormat = NumberFormat.getIntegerInstance(Locale.ENGLISH);
numberFormat.setGroupingUsed(false);
return numberFormat;
}
public static final NumberFormat INT_FORMAT = genIntFormat();
public static int toUnsignedInt(float f) {
int i;
try {
i = Integer.parseUnsignedInt(INT_FORMAT.format(Math.floor(f)));
} catch (NumberFormatException e) {
i = 0;
}
return i;
}
However, this seems a bit too patchwork for me. I'm sure there's a better way of doing this, but I can't seem to find anything online, and ChatGPT just spouts nonsense like it always does. Is there a better way to convert, and what is it? "Better" in my case means faster.
Edit:
0.0f
should give 0
.-1.5f
should give 0
.NaN
should give 0
.1.7f
should give 1
.3e+9f
should give -1294967296
(3 billion as a signed two's complement).1e+12f
should probably give 0
.if
statementI am converting the comments by @Anonymous and the code they link to to this answer. If your float
is >= 0, cast to long
first, and if within the unsigned int
range, cast further to int
. Otherwise the result is 0.
public static int convertFloatToUnsignedInt(float f) {
if (f >= 0) {
long asLong = (long) f;
// Within range of unsigned int?
if (asLong < 0x1_0000_0000L) {
return (int) asLong;
}
}
return 0;
}
Let’s try it with your example float
values:
float[] exampleFloats = { 0.0f, -0.1f, -1.5f, Float.NaN, 1.7f, 3e+9f, 1e+12f };
for (float exampleFloat : exampleFloats) {
int asInt = convertFloatToUnsignedInt(exampleFloat);
System.out.format(Locale.ENGLISH, "%14.1f -> %10s or %11d%n",
exampleFloat, Integer.toUnsignedString(asInt), asInt);
}
Output is
0.0 -> 0 or 0
-0.1 -> 0 or 0
-1.5 -> 0 or 0
NaN -> 0 or 0
1.7 -> 1 or 1
3000000000.0 -> 3000000000 or -1294967296
999999995904.0 -> 0 or 0
The output shows the float
value, the unsigned int
value converted to and the signed value of the same int
. We see that a float
is not able to represent the value 1e+12f
precisely and prints as 999999995904.0.
Repeating the link by @Anonymous the code is running online here.
double
to unsigned long
… but what about double to long?
For the case of converting double
to unsigned long
using the same criteria, you may convert via BigDecimal
. Using the same idea as above:
private static final BigInteger MAX_UNSIGNED_LONG_PLUS_ONE_AS_BIG_INTEGER
= BigInteger.ONE.shiftLeft(64);
private static final BigDecimal MAX_UNSIGNED_LONG_PLUS_ONE
= new BigDecimal(MAX_UNSIGNED_LONG_PLUS_ONE_AS_BIG_INTEGER);
public static long convertDoubleToUnsignedLong(double d) {
if (d >= 0) {
BigDecimal asBigDecimal = new BigDecimal(d);
// Within unsigned long range?
if (asBigDecimal.compareTo(MAX_UNSIGNED_LONG_PLUS_ONE) < 0) {
return asBigDecimal.longValue();
}
}
return 0;
}
(I wasn’t sure whether the code by @Anonymous handles edge cases around the upper limit of the range correctly, so I wrote the above code instead.)
Trying this out too:
double[] exampleDoubles = { 0.0, -1.5, Double.NaN, 1e+19, 1e+20 };
for (double exampleDouble : exampleDoubles) {
long asLong = convertDoubleToUnsignedLong(exampleDouble);
System.out.format(Locale.ENGLISH, "%23.1f -> %20s or %20d%n",
exampleDouble, Long.toUnsignedString(asLong), asLong);
}
Output:
0.0 -> 0 or 0
-1.5 -> 0 or 0
NaN -> 0 or 0
10000000000000000000.0 -> 10000000000000000000 or -8446744073709551616
100000000000000000000.0 -> 0 or 0
I tried your code too. It does not seem to be correct. In the case of 2.2f
taking the floor()
yields 2.0, which you convert to float
again and then to a String
. The String
is 2.0
(not just 2
). Because of the decimal point Integer.parseUnsignedInt()
cannot parse the string and always throws the NumberFormatException
. So I believe you will get 0 always.