From a109f475ed362729c6c5aa109cac6ba759e12330 Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Tue, 2 May 2023 23:49:40 -0400 Subject: [PATCH 1/7] Fix broken/incomplete floating point parsing - Fractional digit scale was broken - Base was partially ignored - Exponent sign was ignored - Exponent for hex float is 2 not 10 --- src/cc65/scanner.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 36fd1301b..634ec39bb 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -687,20 +687,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); } @@ -712,12 +713,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); @@ -747,9 +751,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))); } } From a686d1fa8e4a69cca3761411201874dce3a3059c Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Tue, 2 May 2023 23:50:01 -0400 Subject: [PATCH 2/7] Allow unary +/- for floating point constants --- src/cc65/expr.c | 51 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 691010b0a..42b9cda53 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); From 52f0e6a29cb946da2fca88740012981de3814f51 Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Tue, 2 May 2023 23:57:32 -0400 Subject: [PATCH 3/7] Allow floating point constants to be converted to integer (warning if loss of precision) --- src/cc65/typeconv.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index f77ec3951..004072c03 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -123,6 +123,16 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) ** 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) + Warning ("Floating point constant (%f) converted to integer loses precision (%d)",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. From 7ff74b2c472d5feff62e228a8eebb22d6ccdbc2b Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Wed, 3 May 2023 00:06:09 -0400 Subject: [PATCH 4/7] Give a better error for unsupported floating point arithmetic, instead of internal "Precondition violated" error. --- src/cc65/datatype.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 6907ee099..103a3a634 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); From e3887d7ead27ac092045b0e54e639b288513b163 Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Wed, 3 May 2023 00:12:36 -0400 Subject: [PATCH 5/7] Test to demonstrate availability of floating point constants, document the possibility. --- doc/cc65.sgml | 3 +++ test/val/float-const-convert.c | 14 ++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 test/val/float-const-convert.c diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 683249bda..af85a057f 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -805,6 +805,9 @@ and the one defined by the ISO standard: 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.

C Functions may not return structs (or unions), and structs may not be passed as parameters by value. However, struct assignment *is* 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; +} From 2ac9c6f51efd8a6a89e147d2f4a3de13696cbfcd Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Wed, 3 May 2023 01:44:49 -0400 Subject: [PATCH 6/7] Suppress the floating point precision warning if an explicit cast is used --- src/cc65/typeconv.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 004072c03..4e2249836 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -55,7 +55,7 @@ -static void DoConversion (ExprDesc* Expr, const Type* NewType) +static void DoConversion (ExprDesc* Expr, const Type* NewType, int Explicit) /* Emit code to convert the given expression to a new type. */ { const Type* OldType; @@ -128,8 +128,9 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) */ if (IsClassFloat (OldType) && IsClassInt (NewType)) { long IVal = (long)Expr->V.FVal.V; - if (Expr->V.FVal.V != FP_D_FromInt(IVal).V) + if ((Expr->V.FVal.V != FP_D_FromInt(IVal).V) && !Explicit) { Warning ("Floating point constant (%f) converted to integer loses precision (%d)",Expr->V.FVal.V,IVal); + } Expr->IVal = IVal; } @@ -293,7 +294,7 @@ void TypeConversion (ExprDesc* Expr, const Type* NewType) /* Both types must be complete */ if (!IsIncompleteESUType (NewType) && !IsIncompleteESUType (Expr->Type)) { /* Do the actual conversion */ - DoConversion (Expr, NewType); + DoConversion (Expr, NewType, 0); } else { /* We should have already generated error elsewhere so that we ** could just silently fail here to avoid excess errors, but to @@ -340,7 +341,7 @@ void TypeCast (ExprDesc* Expr) ReplaceType (Expr, NewType); } else if (IsCastType (Expr->Type)) { /* Convert the value. The result has always the new type */ - DoConversion (Expr, NewType); + DoConversion (Expr, NewType, 1); } else { TypeCompatibilityDiagnostic (NewType, Expr->Type, 1, "Cast to incompatible type '%s' from '%s'"); From dbdadaa3f3c647476723b5ed2bb80806e5543457 Mon Sep 17 00:00:00 2001 From: bbbradsmith Date: Wed, 3 May 2023 16:59:48 -0400 Subject: [PATCH 7/7] accidental tabs, printf long expectts explicit %ld --- src/cc65/datatype.c | 4 ++-- src/cc65/typeconv.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 103a3a634..9334db40b 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -671,8 +671,8 @@ const Type* ArithmeticConvert (const Type* lhst, const Type* rhst) ** The integral promotions are performed on both operands. */ if (IsClassFloat(lhst) || IsClassFloat(rhst)) { - Error ("Floating point arithmetic not supported."); - return type_long; + Error ("Floating point arithmetic not supported."); + return type_long; } lhst = IntPromotion (lhst); rhst = IntPromotion (rhst); diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 4e2249836..1003b3c90 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -129,7 +129,7 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType, int Explicit) 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 (%d)",Expr->V.FVal.V,IVal); + Warning ("Floating point constant (%f) converted to integer loses precision (%ld)",Expr->V.FVal.V,IVal); } Expr->IVal = IVal; }