Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SI-10148 Follow Java for float literals #5648

Merged
merged 1 commit into from
Feb 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1263,8 +1263,8 @@ self =>
case CHARLIT => in.charVal
case INTLIT => in.intVal(isNegated).toInt
case LONGLIT => in.intVal(isNegated)
case FLOATLIT => in.floatVal(isNegated).toFloat
case DOUBLELIT => in.floatVal(isNegated)
case FLOATLIT => in.floatVal(isNegated)
case DOUBLELIT => in.doubleVal(isNegated)
case STRINGLIT | STRINGPART => in.strVal.intern()
case TRUE => true
case FALSE => false
Expand Down
34 changes: 28 additions & 6 deletions src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
Original file line number Diff line number Diff line change
Expand Up @@ -972,23 +972,45 @@ trait Scanners extends ScannersCommon {

def intVal: Long = intVal(negated = false)

/** Convert current strVal, base to double value
/** Convert current strVal, base to float value.
*/
def floatVal(negated: Boolean): Double = {
val limit: Double = if (token == DOUBLELIT) Double.MaxValue else Float.MaxValue
def floatVal(negated: Boolean): Float = {
try {
val value: Double = java.lang.Double.valueOf(strVal).doubleValue()
if (value > limit)
val value: Float = java.lang.Float.parseFloat(strVal)
if (value > Float.MaxValue)
syntaxError("floating point number too large")
val zeroly = "0.fF"
som-snytt marked this conversation as resolved.
Show resolved Hide resolved
if (value == 0.0f && strVal.exists(c => !zeroly.contains(c)))
syntaxError("floating point number too small")
if (negated) -value else value
} catch {
case _: NumberFormatException =>
syntaxError("malformed floating point number")
0.0f
}
}

def floatVal: Float = floatVal(negated = false)

/** Convert current strVal, base to double value.
*/
def doubleVal(negated: Boolean): Double = {
try {
val value: Double = java.lang.Double.parseDouble(strVal)
if (value > Double.MaxValue)
syntaxError("double precision floating point number too large")
val zeroly = "0.dD"
if (value == 0.0d && strVal.exists(c => !zeroly.contains(c)))
syntaxError("double precision floating point number too small")
if (negated) -value else value
} catch {
case _: NumberFormatException =>
syntaxError("malformed double precision floating point number")
0.0
}
}

def floatVal: Double = floatVal(negated = false)
def doubleVal: Double = doubleVal(negated = false)

def checkNoLetter(): Unit = {
if (isIdentifierPart(ch) && ch >= ' ')
Expand Down
14 changes: 13 additions & 1 deletion test/files/neg/literals.check
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ literals.scala:23: error: missing integer number
literals.scala:27: error: Decimal integer literals may not have a leading zero. (Octal syntax is obsolete.)
def tooManyZeros: Int = 00 // line 26: no leading zero
^
literals.scala:40: error: floating point number too small
def tooTiny: Float = { 0.7e-45f } // floating point number too small
^
literals.scala:42: error: double precision floating point number too small
def twoTiny: Double = { 2.0e-324 } // double precision floating point number too small
^
literals.scala:44: error: floating point number too large
def tooHuge: Float = { 3.4028236E38f } // floating point number too large
^
literals.scala:46: error: double precision floating point number too large
def twoHuge: Double = { 1.7976931348623159e308 } // double precision floating point number too large
^
literals.scala:14: error: identifier expected but '}' found.
def orphanDot: Int = { 9. } // line 12: ident expected
^
Expand All @@ -37,4 +49,4 @@ literals.scala:29: error: ';' expected but 'def' found.
literals.scala:33: error: identifier expected but 'def' found.
def zeroOfNineDot: Int = 09. // line 32: malformed integer, ident expected
^
13 errors found
17 errors found
13 changes: 12 additions & 1 deletion test/files/neg/literals.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

/* This took me literally all day.
*/
*/
trait RejectedLiterals {

def missingHex: Int = { 0x } // line 4: was: not reported, taken as zero
Expand Down Expand Up @@ -34,3 +34,14 @@ trait Braceless {

def noHexFloat: Double = 0x1.2 // line 34: ';' expected but double literal found.
}

trait MoreSadness {

def tooTiny: Float = { 0.7e-45f } // floating point number too small

def twoTiny: Double = { 2.0e-324 } // double precision floating point number too small

def tooHuge: Float = { 3.4028236E38f } // floating point number too large

def twoHuge: Double = { 1.7976931348623159e308 } // double precision floating point number too large
}
5 changes: 5 additions & 0 deletions test/files/run/literals.scala
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ object Test {
check_success("3.14f == 3.14f", 3.14f, 3.14f)
check_success("6.022e23f == 6.022e23f", 6.022e23f, 6.022e23f)
check_success("09f == 9.0f", 09f, 9.0f)
check_success("1.00000017881393421514957253748434595763683319091796875001f == 1.0000001f",
1.00000017881393421514957253748434595763683319091796875001f,
1.0000001f)
check_success("3.4028235E38f == Float.MaxValue", 3.4028235E38f, Float.MaxValue)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why no check using Float.MinValue? Probably demonstrating my ignorance of something here!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My name isn't ScalaCheck. Also, the difference is just a sign bit which is turned on by a leading midlevel horizontal bar symbol. Of course, the test wouldn't hurt.

Copy link
Member

@janekdb janekdb Jan 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignorance of IEEE 754 fully demoed (by me)! Java MIN_VALUE is not the negative float with the greatest magnitude but the smallest positive nonzero value.

https://docs.oracle.com/javase/7/docs/api/java/lang/Float.html#MIN_VALUE

Having said that why not add a check for the effect of a leading hyphen-minus?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, if I have time to switch my repo back to this branch on my old home machine.

There's a puzzler (BTW) that turns on the fact that -1.0f is a literal. See here.

check_success("1.asInstanceOf[Float] == 1.0", 1.asInstanceOf[Float], 1.0f)
check_success("1l.asInstanceOf[Float] == 1.0", 1l.asInstanceOf[Float], 1.0f)

Expand All @@ -97,6 +101,7 @@ object Test {
check_success("3.14 == 3.14", 3.14, 3.14)
check_success("1e-9d == 1.0e-9", 1e-9d, 1.0e-9)
check_success("1e137 == 1.0e137", 1e137, 1.0e137)
check_success("1.7976931348623157e308d == Double.MaxValue", 1.7976931348623157e308d, Double.MaxValue)
check_success("1.asInstanceOf[Double] == 1.0", 1.asInstanceOf[Double], 1.0)
check_success("1l.asInstanceOf[Double] == 1.0", 1l.asInstanceOf[Double], 1.0)

Expand Down