fix float to integer conversion

Java requires that NaNs be converted to zero and that numbers at or
beyond the limits of integer representation be clamped to the largest
or smallest value that can be represented, respectively.
This commit is contained in:
Joel Dice 2012-08-12 14:31:58 -06:00
parent 47503854d5
commit b98abe3f94
6 changed files with 132 additions and 10 deletions

View File

@ -14,6 +14,10 @@ public final class Float extends Number {
public static final Class TYPE = Class.forCanonicalName("F");
private static final int EXP_BIT_MASK = 0x7F800000;
private static final int SIGNIF_BIT_MASK = 0x007FFFFF;
public static final float NEGATIVE_INFINITY = -1.0f / 0.0f;
public static final float POSITIVE_INFINITY = 1.0f / 0.0f;
public static final float NaN = 0.0f / 0.0f;
private final float value;

View File

@ -2379,7 +2379,11 @@ class MyArchitecture: public Assembler::Architecture {
break;
case Float2Int:
if (vfpSupported() && bSize == 4) {
// todo: Java requires different semantics than SSE for
// converting floats to integers, we we need to either use
// thunks or produce inline machine code which handles edge
// cases properly.
if (false && vfpSupported() && bSize == 4) {
*aTypeMask = (1 << RegisterOperand);
*aRegisterMask = FPR_MASK64;
} else {

View File

@ -2587,13 +2587,25 @@ doubleToFloat(int64_t a)
int64_t
doubleToInt(int64_t a)
{
return static_cast<int32_t>(bitsToDouble(a));
double f = bitsToDouble(a);
switch (fpclassify(f)) {
case FP_NAN: return 0;
case FP_INFINITE: return isinf(f) == 1 ? INT32_MAX : INT32_MIN;
default: return f >= INT32_MAX ? INT32_MAX
: (f <= INT32_MIN ? INT32_MIN : static_cast<int32_t>(f));
}
}
int64_t
doubleToLong(int64_t a)
{
return static_cast<int64_t>(bitsToDouble(a));
double f = bitsToDouble(a);
switch (fpclassify(f)) {
case FP_NAN: return 0;
case FP_INFINITE: return isinf(f) == 1 ? INT64_MAX : INT64_MIN;
default: return f >= INT64_MAX ? INT64_MAX
: (f <= INT64_MIN ? INT64_MIN : static_cast<int64_t>(f));
}
}
uint64_t
@ -2735,13 +2747,24 @@ floatToDouble(int32_t a)
int64_t
floatToInt(int32_t a)
{
return static_cast<int32_t>(bitsToFloat(a));
float f = bitsToFloat(a);
switch (fpclassify(f)) {
case FP_NAN: return 0;
case FP_INFINITE: return isinf(f) == 1 ? INT32_MAX : INT32_MIN;
default: return f >= INT32_MAX ? INT32_MAX
: (f <= INT32_MIN ? INT32_MIN : static_cast<int32_t>(f));
}
}
int64_t
floatToLong(int32_t a)
{
return static_cast<int64_t>(bitsToFloat(a));
float f = bitsToFloat(a);
switch (fpclassify(f)) {
case FP_NAN: return 0;
case FP_INFINITE: return isinf(f) == 1 ? INT64_MAX : INT64_MIN;
default: return static_cast<int64_t>(f);
}
}
uint64_t

View File

@ -1078,11 +1078,28 @@ interpret3(Thread* t, const int base)
} goto loop;
case d2i: {
pushInt(t, static_cast<int32_t>(popDouble(t)));
double f = popDouble(t);
switch (fpclassify(f)) {
case FP_NAN: pushInt(t, 0); break;
case FP_INFINITE: pushInt(t, isinf(f) == 1 ? INT32_MAX : INT32_MIN); break;
default: pushInt
(t, f >= INT32_MAX ? INT32_MAX
: (f <= INT32_MIN ? INT32_MIN : static_cast<int32_t>(f)));
break;
}
} goto loop;
case d2l: {
pushLong(t, static_cast<int64_t>(popDouble(t)));
double f = popDouble(t);
switch (fpclassify(f)) {
case FP_NAN: pushLong(t, 0); break;
case FP_INFINITE: pushLong(t, isinf(f) == 1 ? INT64_MAX : INT64_MIN);
break;
default: pushLong
(t, f >= INT64_MAX ? INT64_MAX
: (f <= INT64_MIN ? INT64_MIN : static_cast<int64_t>(f)));
break;
}
} goto loop;
case dadd: {
@ -1278,11 +1295,24 @@ interpret3(Thread* t, const int base)
} goto loop;
case f2i: {
pushInt(t, static_cast<int32_t>(popFloat(t)));
float f = popFloat(t);
switch (fpclassify(f)) {
case FP_NAN: pushInt(t, 0); break;
case FP_INFINITE: pushInt(t, isinf(f) == 1 ? INT32_MAX : INT32_MIN); break;
default: pushInt(t, f >= INT32_MAX ? INT32_MAX
: (f <= INT32_MIN ? INT32_MIN : static_cast<int32_t>(f)));
break;
}
} goto loop;
case f2l: {
pushLong(t, static_cast<int64_t>(popFloat(t)));
float f = popFloat(t);
switch (fpclassify(f)) {
case FP_NAN: pushLong(t, 0); break;
case FP_INFINITE: pushLong(t, isinf(f) == 1 ? INT64_MAX : INT64_MIN);
break;
default: pushLong(t, static_cast<int64_t>(f)); break;
}
} goto loop;
case fadd: {

View File

@ -3064,7 +3064,11 @@ class MyArchitecture: public Assembler::Architecture {
break;
case Float2Int:
if (useSSE(&c) and bSize <= TargetBytesPerWord) {
// todo: Java requires different semantics than SSE for
// converting floats to integers, we we need to either use
// thunks or produce inline machine code which handles edge
// cases properly.
if (false and useSSE(&c) and bSize <= TargetBytesPerWord) {
*aTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand);
*aRegisterMask = (static_cast<uint64_t>(FloatRegisterMask) << 32)
| FloatRegisterMask;

View File

@ -272,5 +272,62 @@ public class Floats {
}
}
}
{ double v = Double.NaN;
expect(0 == (int) v); }
{ double v = Double.NEGATIVE_INFINITY;
expect(Integer.MIN_VALUE == (int) v); }
{ double v = Long.MIN_VALUE;
expect(Integer.MIN_VALUE == (int) v); }
{ double v = Double.POSITIVE_INFINITY;
expect(Integer.MAX_VALUE == (int) v); }
{ double v = Long.MAX_VALUE;
expect(Integer.MAX_VALUE == (int) v); }
{ float v = Float.NaN;
expect(0 == (int) v); }
{ float v = Float.NEGATIVE_INFINITY;
expect(Integer.MIN_VALUE == (int) v); }
{ float v = Integer.MIN_VALUE;
expect(Integer.MIN_VALUE == (int) v); }
{ float v = Float.POSITIVE_INFINITY;
expect(Integer.MAX_VALUE == (int) v); }
{ float v = Integer.MAX_VALUE;
expect(Integer.MAX_VALUE == (int) v); }
{ double v = Double.NaN;
expect(0 == (long) v); }
{ double v = Double.NEGATIVE_INFINITY;
expect(Long.MIN_VALUE == (long) v); }
{ double v = Long.MIN_VALUE;
expect(Long.MIN_VALUE == (long) v); }
{ double v = Double.POSITIVE_INFINITY;
expect(Long.MAX_VALUE == (long) v); }
{ double v = Long.MAX_VALUE;
expect(Long.MAX_VALUE == (long) v); }
{ float v = Float.NaN;
expect(0 == (long) v); }
{ float v = Float.NEGATIVE_INFINITY;
expect(Long.MIN_VALUE == (long) v); }
{ float v = Integer.MIN_VALUE;
expect(Integer.MIN_VALUE == (long) v); }
{ float v = Float.POSITIVE_INFINITY;
expect(Long.MAX_VALUE == (long) v); }
}
}