diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs index 68a980556..27c6edb43 100644 --- a/src/MySqlConnector/Core/Row.cs +++ b/src/MySqlConnector/Core/Row.cs @@ -353,8 +353,15 @@ public double GetDouble(int ordinal) public float GetFloat(int ordinal) { var value = GetValue(ordinal); - return value is decimal decimalValue ? (float) decimalValue : - (float) value; + + // Loss of precision is expected, significant loss of information is not. + // Use explicit range checks to guard against that. + return value switch + { + double doubleValue => (doubleValue >= float.MinValue && doubleValue <= float.MaxValue ? (float) doubleValue : throw new InvalidCastException("The value cannot be safely cast to Single.")), + decimal decimalValue => (float) decimalValue, + _ => (float) value + }; } public MySqlDateTime GetMySqlDateTime(int ordinal) diff --git a/tests/Conformance.Tests/GetValueConversionTests.cs b/tests/Conformance.Tests/GetValueConversionTests.cs index 2770ce101..283e6b37f 100644 --- a/tests/Conformance.Tests/GetValueConversionTests.cs +++ b/tests/Conformance.Tests/GetValueConversionTests.cs @@ -82,5 +82,13 @@ public GetValueConversionTests(SelectValueFixture fixture) // the minimum date permitted by MySQL is 1000-01-01; override the minimum value for DateTime tests public override void GetDateTime_for_minimum_Date() => TestGetValue(DbType.Date, ValueKind.Minimum, x => x.GetDateTime(0), new DateTime(1000, 1, 1)); public override void GetDateTime_for_minimum_DateTime() => TestGetValue(DbType.Date, ValueKind.Minimum, x => x.GetDateTime(0), new DateTime(1000, 1, 1)); + + // The GetFloat() implementation allows for conversions from double to float. + // The minimum tests for float and double do not test for the smallest possible value (as the tests for integer values do), + // but test for the largest value smaller than 0 (Epsilon). + // If double.Epsilon is converted to float, it will result in 0. + public override void GetFloat_throws_for_minimum_Double() => TestGetValue(DbType.Double, ValueKind.Minimum, x => x.GetFloat(0), 0); + public override void GetFloat_throws_for_one_Double() => TestGetValue(DbType.Double, ValueKind.One, x => x.GetFloat(0), 1); + public override void GetFloat_throws_for_zero_Double() => TestGetValue(DbType.Double, ValueKind.Zero, x => x.GetFloat(0), 0); } }