diff --git a/src/main/java/org/openrewrite/staticanalysis/PrimitiveWrapperClassConstructorToValueOf.java b/src/main/java/org/openrewrite/staticanalysis/PrimitiveWrapperClassConstructorToValueOf.java index 243d75cee..a3c6ae580 100644 --- a/src/main/java/org/openrewrite/staticanalysis/PrimitiveWrapperClassConstructorToValueOf.java +++ b/src/main/java/org/openrewrite/staticanalysis/PrimitiveWrapperClassConstructorToValueOf.java @@ -91,16 +91,16 @@ public J visitNewClass(J.NewClass newClass, ExecutionContext ctx) { valueOf = JavaTemplate.builder("Short.valueOf(#{any(short)})"); break; case "java.lang.Float": - if (arg instanceof J.Literal && JavaType.Primitive.Double == ((J.Literal) arg).getType()) { - arg = ((J.Literal) arg).withType(JavaType.Primitive.String); - arg = ((J.Literal) arg).withValueSource("\"" + ((J.Literal) arg).getValue() + "\""); - } - JavaType argType = arg.getType(); if (TypeUtils.isOfClassType(argType, "java.lang.Double")) { valueOf = JavaTemplate.builder("Float.valueOf(#{any(java.lang.Double)}.floatValue())"); - } else if (JavaType.Primitive.Double == arg.getType()) { - valueOf = JavaTemplate.builder("Float.valueOf((float) #{any(double)})"); + } else if (JavaType.Primitive.Double == argType) { + // A cast binds tighter than these operators, so unparenthesized it covers only the + // first operand, either failing to compile or rounding one step too early + valueOf = JavaTemplate.builder(arg instanceof J.Binary || arg instanceof J.Ternary || + arg instanceof J.Assignment || arg instanceof J.AssignmentOperation ? + "Float.valueOf((float) (#{any(double)}))" : + "Float.valueOf((float) #{any(double)})"); } else { valueOf = JavaTemplate.builder("Float.valueOf(#{any(float)})"); } diff --git a/src/test/java/org/openrewrite/staticanalysis/PrimitiveWrapperClassConstructorToValueOfTest.java b/src/test/java/org/openrewrite/staticanalysis/PrimitiveWrapperClassConstructorToValueOfTest.java index 31e83bb23..0ff588475 100644 --- a/src/test/java/org/openrewrite/staticanalysis/PrimitiveWrapperClassConstructorToValueOfTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/PrimitiveWrapperClassConstructorToValueOfTest.java @@ -203,7 +203,7 @@ class T { Double d1 = Double.valueOf(1.0); double d2 = 2.0d; void makeFloats() { - Float f = Float.valueOf("2.0"); + Float f = Float.valueOf((float) 2.0d); Float f2 = Float.valueOf(getD().floatValue()); Float f3 = Float.valueOf(d1.floatValue()); Float f4 = Float.valueOf((float) d2); @@ -217,6 +217,127 @@ Double getD() { ); } + @Test + void doubleLiteralToFloatKeepsBinary64Rounding() { + // `new Float(double)` is `(float) value`, rounding through binary64 to 0x3f800000, where + // `Float.valueOf(String)` rounds the decimal straight to binary32 and yields 0x3f800001 + rewriteRun( + //language=java + java( + """ + class T { + Float value = new Float(1.0000000596046448); + } + """, + """ + class T { + Float value = Float.valueOf((float) 1.0000000596046448); + } + """ + ) + ); + } + + @Test + void doubleLiteralToFloatKeepsSourceForm() { + rewriteRun( + //language=java + java( + """ + class T { + Float hex = new Float(0x1.0000002p0); + Float suffixed = new Float(1.0000000596046448D); + Float subnormal = new Float(4.9E-324); + } + """, + """ + class T { + Float hex = Float.valueOf((float) 0x1.0000002p0); + Float suffixed = Float.valueOf((float) 1.0000000596046448D); + Float subnormal = Float.valueOf((float) 4.9E-324); + } + """ + ) + ); + } + + @Test + void doubleExpressionToFloatUsesCast() { + rewriteRun( + //language=java + java( + """ + class T { + Float negativeZero = new Float(-0.0); + Float overflowing = new Float(Double.MAX_VALUE); + Float parenthesized = new Float((1.0000000596046448)); + } + """, + """ + class T { + Float negativeZero = Float.valueOf((float) -0.0); + Float overflowing = Float.valueOf((float) Double.MAX_VALUE); + Float parenthesized = Float.valueOf((float) (1.0000000596046448)); + } + """ + ) + ); + } + + @Test + void compoundDoubleExpressionToFloatIsParenthesized() { + rewriteRun( + //language=java + java( + """ + class T { + double d1 = 1.0; + double d2 = 2.0; + void makeFloats() { + Float sum = new Float(d1 + d2); + Float ternary = new Float(d1 > d2 ? d1 : d2); + Float assigned = new Float(d1 = 2.0); + Float compound = new Float(d1 += 2.0); + } + } + """, + """ + class T { + double d1 = 1.0; + double d2 = 2.0; + void makeFloats() { + Float sum = Float.valueOf((float) (d1 + d2)); + Float ternary = Float.valueOf((float) (d1 > d2 ? d1 : d2)); + Float assigned = Float.valueOf((float) (d1 = 2.0)); + Float compound = Float.valueOf((float) (d1 += 2.0)); + } + } + """ + ) + ); + } + + @Test + void floatLiteralUnchangedByDoubleHandling() { + rewriteRun( + //language=java + java( + """ + class T { + Float f = new Float(1.1f); + Float hex = new Float(0x1.0000002p0f); + } + """, + """ + class T { + Float f = Float.valueOf(1.1f); + Float hex = Float.valueOf(0x1.0000002p0f); + } + """ + ) + ); + } + @Test void withinEnum() { rewriteRun(