diff --git a/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java b/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java
index 24c21acf6af..1b60933326a 100644
--- a/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java
+++ b/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java
@@ -160,14 +160,12 @@ final class SynthesizedMergedAnnotationInvocationHandler i
}
/**
- * This method currently does not address the following issues which we may
+ * This method currently does not address the following issue which we may
* choose to address at a later point in time.
*
*
* - non-ASCII, non-visible, and non-printable characters within a character
* or String literal are not escaped.
- * - formatting for float and double values does not take into account whether
- * a value is not a number (NaN) or infinite.
*
* @param value the attribute value to format
* @return the formatted string representation
@@ -199,10 +197,30 @@ final class SynthesizedMergedAnnotationInvocationHandler i
return Long.toString((Long) value) + 'L';
}
if (type == Float.class) {
- return Float.toString((Float) value) + 'f';
+ float floatValue = (Float) value;
+ if (Float.isNaN(floatValue)) {
+ return "0.0f/0.0f";
+ }
+ if (floatValue == Float.POSITIVE_INFINITY) {
+ return "1.0f/0.0f";
+ }
+ if (floatValue == Float.NEGATIVE_INFINITY) {
+ return "-1.0f/0.0f";
+ }
+ return Float.toString(floatValue) + 'f';
}
if (type == Double.class) {
- return Double.toString((Double) value);
+ double doubleValue = (Double) value;
+ if (Double.isNaN(doubleValue)) {
+ return "0.0/0.0";
+ }
+ if (doubleValue == Double.POSITIVE_INFINITY) {
+ return "1.0/0.0";
+ }
+ if (doubleValue == Double.NEGATIVE_INFINITY) {
+ return "-1.0/0.0";
+ }
+ return Double.toString(doubleValue);
}
if (value instanceof Enum> e) {
return e.name();
diff --git a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java
index c5c386e71d6..61fc0ffe367 100644
--- a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java
+++ b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java
@@ -2060,6 +2060,30 @@ class MergedAnnotationsTests {
.isEqualTo("@%s({\"FromValueAttributeMeta\"})", ValueAttribute.class.getCanonicalName());
}
+ @Test // gh-37244
+ void toStringForSynthesizedAnnotationsWithNonFiniteFloatingPointValues() {
+ Map attributes = Map.of(
+ "name", "test",
+ "floatValue", Float.NaN,
+ "doubleValue", Double.NaN);
+ RequestMapping mapping = MergedAnnotation.of(RequestMapping.class, attributes).synthesize();
+ assertThat(mapping).asString().contains("floatValue=0.0f/0.0f", "doubleValue=0.0/0.0");
+
+ attributes = Map.of(
+ "name", "test",
+ "floatValue", Float.POSITIVE_INFINITY,
+ "doubleValue", Double.POSITIVE_INFINITY);
+ mapping = MergedAnnotation.of(RequestMapping.class, attributes).synthesize();
+ assertThat(mapping).asString().contains("floatValue=1.0f/0.0f", "doubleValue=1.0/0.0");
+
+ attributes = Map.of(
+ "name", "test",
+ "floatValue", Float.NEGATIVE_INFINITY,
+ "doubleValue", Double.NEGATIVE_INFINITY);
+ mapping = MergedAnnotation.of(RequestMapping.class, attributes).synthesize();
+ assertThat(mapping).asString().contains("floatValue=-1.0f/0.0f", "doubleValue=-1.0/0.0");
+ }
+
@Test
void equalsForSynthesizedAnnotations() throws Exception {
Method methodWithPath = WebController.class.getMethod("handleMappedWithPathAttribute");