fix broken float type precision conversion
it is not the case that ln(10) = log_2(10)
This commit is contained in:
parent
36be5cc3da
commit
1172943252
|
@ -161,6 +161,8 @@ import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
|
|
||||||
import jakarta.persistence.TemporalType;
|
import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
|
import static java.lang.Math.ceil;
|
||||||
|
import static java.lang.Math.log;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_END;
|
import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_END;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_START_DATE;
|
import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_START_DATE;
|
||||||
import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_START_TIME;
|
import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_START_TIME;
|
||||||
|
@ -208,6 +210,9 @@ public abstract class Dialect implements ConversionContext {
|
||||||
private static final Pattern ESCAPE_CLOSING_COMMENT_PATTERN = Pattern.compile( "\\*/" );
|
private static final Pattern ESCAPE_CLOSING_COMMENT_PATTERN = Pattern.compile( "\\*/" );
|
||||||
private static final Pattern ESCAPE_OPENING_COMMENT_PATTERN = Pattern.compile( "/\\*" );
|
private static final Pattern ESCAPE_OPENING_COMMENT_PATTERN = Pattern.compile( "/\\*" );
|
||||||
|
|
||||||
|
//needed for converting precision from decimal to binary digits
|
||||||
|
private static final double LOG_BASE2OF10 = log(10)/log(2);
|
||||||
|
|
||||||
private final TypeNames typeNames = new TypeNames();
|
private final TypeNames typeNames = new TypeNames();
|
||||||
private final TypeNames hibernateTypeNames = new TypeNames();
|
private final TypeNames hibernateTypeNames = new TypeNames();
|
||||||
|
|
||||||
|
@ -384,7 +389,7 @@ public abstract class Dialect implements ConversionContext {
|
||||||
return code == Types.FLOAT
|
return code == Types.FLOAT
|
||||||
&& size != null
|
&& size != null
|
||||||
&& size.getPrecision() != null
|
&& size.getPrecision() != null
|
||||||
? Size.precision( (int) Math.ceil( size.getPrecision() / 53.0 * 17.0 ) )
|
? Size.precision( (int) Math.ceil( size.getPrecision() / LOG_BASE2OF10 ) )
|
||||||
: size;
|
: size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3774,20 +3779,24 @@ public abstract class Dialect implements ConversionContext {
|
||||||
case Types.FLOAT:
|
case Types.FLOAT:
|
||||||
case Types.DOUBLE:
|
case Types.DOUBLE:
|
||||||
case Types.REAL:
|
case Types.REAL:
|
||||||
// The given precision and scale are in decimal numbers as per Javadoc of jakarta.persistence.Column
|
// this is almost always the thing we use:
|
||||||
// but the SQL type FLOAT takes the precision in binary digits,
|
size.setPrecision( javaType.getDefaultSqlPrecision( Dialect.this, jdbcType ) );
|
||||||
// so we have to calculate the number of binary digits necessary.
|
if (scale != null && scale!=0) {
|
||||||
// If the precision and a scale are given, we assume the values are given as decimal digits.
|
throw new IllegalArgumentException("scale has no meaning for floating point numbers");
|
||||||
// If just the precision is given, we assume the value is in binary digits already.
|
|
||||||
if ( precision != null && scale != null ) {
|
|
||||||
scale = null;
|
|
||||||
// See https://stackoverflow.com/questions/17415847/how-does-float-map-relate-to-number-in-oracle-10g/17416421
|
|
||||||
// for the formula which was inverted to calculate the binary digit count
|
|
||||||
precision = (int) Math.ceil( precision * Math.log( 10 ) );
|
|
||||||
}
|
}
|
||||||
|
// but if the user explicitly specifies a precision, we need to convert it:
|
||||||
|
if (precision != null) {
|
||||||
|
// convert from base 10 (as specified in @Column) to base 2 (as specified by SQL)
|
||||||
|
// using the magic of high school math: log_2(10^n) = n*log_2(10) = n*ln(10)/ln(2)
|
||||||
|
precision = (int) ceil( precision * LOG_BASE2OF10 );
|
||||||
|
}
|
||||||
|
break;
|
||||||
case Types.TIMESTAMP:
|
case Types.TIMESTAMP:
|
||||||
case Types.TIMESTAMP_WITH_TIMEZONE:
|
case Types.TIMESTAMP_WITH_TIMEZONE:
|
||||||
size.setPrecision( javaType.getDefaultSqlPrecision( Dialect.this, jdbcType ) );
|
size.setPrecision( javaType.getDefaultSqlPrecision( Dialect.this, jdbcType ) );
|
||||||
|
if (scale != null && scale!=0) {
|
||||||
|
throw new IllegalArgumentException("scale has no meaning for timestamps");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Types.NUMERIC:
|
case Types.NUMERIC:
|
||||||
case Types.DECIMAL:
|
case Types.DECIMAL:
|
||||||
|
|
|
@ -31,7 +31,7 @@ public class Discount implements Serializable {
|
||||||
private Customer owner;
|
private Customer owner;
|
||||||
|
|
||||||
|
|
||||||
@Column(precision = 5, scale = 2)
|
@Column(precision = 5)
|
||||||
public double getDiscount() {
|
public double getDiscount() {
|
||||||
return discount;
|
return discount;
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,7 +155,7 @@ public class OneToManyCompositeKeyTest extends BaseEnversJPAFunctionalTestCase {
|
||||||
@EmbeddedId
|
@EmbeddedId
|
||||||
private DesignContractId pk = new DesignContractId();
|
private DesignContractId pk = new DesignContractId();
|
||||||
@Basic
|
@Basic
|
||||||
@Column(name = "GOAL", nullable = false, precision = 5, scale = 2)
|
@Column(name = "GOAL", nullable = false, precision = 5)
|
||||||
private Double goal;
|
private Double goal;
|
||||||
|
|
||||||
DesignContract() {
|
DesignContract() {
|
||||||
|
|
Loading…
Reference in New Issue