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;
+}