diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 4303edb48..e1e9214e5 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -807,6 +807,9 @@ and the one defined by the ISO standard: <itemize> <item> The datatypes "float" and "double" are not available. + Floating point constants may be used, though they will have to be + converted and stored into integer values. + Floating point arithmetic expressions are not supported. <p> <item> C Functions may pass and return structs (or unions) by value, but only of 1, 2 or 4 byte sizes. diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index caa41a7a4..37fe54023 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -670,6 +670,10 @@ const Type* ArithmeticConvert (const Type* lhst, const Type* rhst) ** floating point types are not (yet) supported. ** The integral promotions are performed on both operands. */ + if (IsClassFloat(lhst) || IsClassFloat(rhst)) { + Error ("Floating point arithmetic not supported."); + return type_long; + } lhst = IntPromotion (lhst); rhst = IntPromotion (rhst); diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 9460569ed..fa6f21fb2 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -1901,31 +1901,46 @@ static void UnaryOp (ExprDesc* Expr) /* Get the expression */ hie10 (Expr); - /* We can only handle integer types */ - if (!IsClassInt (Expr->Type)) { - Error ("Argument must have integer type"); - ED_MakeConstAbsInt (Expr, 1); - } - /* Check for a constant numeric expression */ if (ED_IsConstAbs (Expr)) { - /* Value is numeric */ - switch (Tok) { - case TOK_MINUS: Expr->IVal = -Expr->IVal; break; - case TOK_PLUS: break; - case TOK_COMP: Expr->IVal = ~Expr->IVal; break; - default: Internal ("Unexpected token: %d", Tok); + + if (IsClassFloat (Expr->Type)) { + switch (Tok) { + case TOK_MINUS: Expr->V.FVal = FP_D_Sub(FP_D_Make(0.0),Expr->V.FVal); break; + case TOK_PLUS: break; + case TOK_COMP: Error ("Unary ~ operator not valid for floating point constant"); break; + default: Internal ("Unexpected token: %d", Tok); + } + } else { + if (!IsClassInt (Expr->Type)) { + Error ("Constant argument must have integer or float type"); + ED_MakeConstAbsInt (Expr, 1); + } + + /* Value is numeric */ + switch (Tok) { + case TOK_MINUS: Expr->IVal = -Expr->IVal; break; + case TOK_PLUS: break; + case TOK_COMP: Expr->IVal = ~Expr->IVal; break; + default: Internal ("Unexpected token: %d", Tok); + } + + /* Adjust the type of the expression */ + Expr->Type = IntPromotion (Expr->Type); + + /* Limit the calculated value to the range of its type */ + LimitExprValue (Expr, 1); } - /* Adjust the type of the expression */ - Expr->Type = IntPromotion (Expr->Type); - - /* Limit the calculated value to the range of its type */ - LimitExprValue (Expr, 1); - } else { unsigned Flags; + /* If not constant, we can only handle integer types */ + if (!IsClassInt (Expr->Type)) { + Error ("Non-constant argument must have integer type"); + ED_MakeConstAbsInt (Expr, 1); + } + /* Value is not constant */ LoadExpr (CF_NONE, Expr); diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index ede77cb2c..bf3d9365d 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -704,20 +704,21 @@ static void NumericConst (void) /* Check for a fractional part and read it */ if (SB_Peek (&Src) == '.') { - Double Scale; + Double Scale, ScaleDigit; /* Skip the dot */ SB_Skip (&Src); /* Read fractional digits */ - Scale = FP_D_Make (1.0); + ScaleDigit = FP_D_Div (FP_D_Make (1.0), FP_D_FromInt (Base)); + Scale = ScaleDigit; while (IsXDigit (SB_Peek (&Src)) && (DigitVal = HexVal (SB_Peek (&Src))) < Base) { /* Get the value of this digit */ - Double FracVal = FP_D_Div (FP_D_FromInt (DigitVal * Base), Scale); + Double FracVal = FP_D_Mul (FP_D_FromInt (DigitVal), Scale); /* Add it to the float value */ FVal = FP_D_Add (FVal, FracVal); - /* Scale base */ - Scale = FP_D_Mul (Scale, FP_D_FromInt (DigitVal)); + /* Adjust Scale for next digit */ + Scale = FP_D_Mul (Scale, ScaleDigit); /* Skip the digit */ SB_Skip (&Src); } @@ -729,12 +730,15 @@ static void NumericConst (void) unsigned Digits; unsigned Exp; + int Sign; /* Skip the exponent notifier */ SB_Skip (&Src); /* Read an optional sign */ + Sign = 0; if (SB_Peek (&Src) == '-') { + Sign = 1; SB_Skip (&Src); } else if (SB_Peek (&Src) == '+') { SB_Skip (&Src); @@ -764,9 +768,11 @@ static void NumericConst (void) Warning ("Floating constant exponent is too large"); } - /* Scale the exponent and adjust the value accordingly */ + /* Scale the exponent and adjust the value accordingly. + ** Decimal exponents are base 10, hexadecimal exponents are base 2 (C99). + */ if (Exp) { - FVal = FP_D_Mul (FVal, FP_D_Make (pow (10, Exp))); + FVal = FP_D_Mul (FVal, FP_D_Make (pow ((Base == 16) ? 2.0 : 10.0, (Sign ? -1.0 : 1.0) * Exp))); } } diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 6bdb45b5f..d82d683fb 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -123,6 +123,17 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType, int Explicit) ** to handle sign extension correctly. */ + /* If this is a floating point constant, convert to integer, + ** and warn if precision is discarded. + */ + if (IsClassFloat (OldType) && IsClassInt (NewType)) { + long IVal = (long)Expr->V.FVal.V; + if ((Expr->V.FVal.V != FP_D_FromInt(IVal).V) && !Explicit) { + Warning ("Floating point constant (%f) converted to integer loses precision (%ld)",Expr->V.FVal.V,IVal); + } + Expr->IVal = IVal; + } + /* Check if the new datatype will have a smaller range. If it ** has a larger range, things are OK, since the value is ** internally already represented by a long. diff --git a/test/val/float-const-convert.c b/test/val/float-const-convert.c new file mode 100644 index 000000000..729595e0f --- /dev/null +++ b/test/val/float-const-convert.c @@ -0,0 +1,14 @@ +/* Demonstrates that floating point constants are allowed in a limited way. + Value will be converted to an int, with a warning if precision is lost. */ + +int a = 3.0; +int b = 23.1; +int c = -5.0; + +int main(void) +{ + if (a != 3) return 1; + if (b != 23) return 2; + if (c != -5) return 3; + return 0; +}