Support binary and octal floating-point
authorH. Peter Anvin <hpa@zytor.com>
Tue, 23 Oct 2007 00:34:10 +0000 (17:34 -0700)
committerH. Peter Anvin <hpa@zytor.com>
Tue, 23 Oct 2007 00:34:10 +0000 (17:34 -0700)
For consistency, support binary and octal floating-point, and accept
a "0d" or "0t" prefix for decimal floating-point.  However, we do not
accept a binary exponent (p) for a decimal mantissa, or vice versa.

float.c
stdscan.c
test/radix.asm

diff --git a/float.c b/float.c
index a2df323..7e99f96 100644 (file)
--- a/float.c
+++ b/float.c
@@ -424,29 +424,33 @@ static bool ieee_round(int sign, uint16_t * mant, int32_t i)
     return false;
 }
 
-static int hexval(char c)
+/* Returns a value >= 16 if not a valid hex digit */
+static unsigned int hexval(char c)
 {
-    if (c >= '0' && c <= '9')
-        return c - '0';
-    else if (c >= 'a' && c <= 'f')
-        return c - 'a' + 10;
+    unsigned int v = (unsigned char) c;
+
+    if (v >= '0' && v <= '9')
+        return v - '0';
     else
-        return c - 'A' + 10;
+        return (v|0x20) - 'a' + 10;
 }
 
-static bool ieee_flconvert_hex(const char *string, uint16_t * mant,
-                               int32_t * exponent)
+/* Handle floating-point numbers with radix 2^bits and binary exponent */
+static bool ieee_flconvert_bin(const char *string, int bits,
+                              uint16_t * mant, int32_t * exponent)
 {
     static const int log2tbl[16] =
         { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
     uint16_t mult[MANT_WORDS + 1], *mp;
     int ms;
     int32_t twopwr;
-    int seendot, seendigit;
+    bool seendot, seendigit;
     unsigned char c;
+    int radix = 1 << bits;
+    unsigned int v;
 
     twopwr = 0;
-    seendot = seendigit = 0;
+    seendot = seendigit = false;
     ms = 0;
     mp = NULL;
 
@@ -461,17 +465,15 @@ static bool ieee_flconvert_hex(const char *string, uint16_t * mant,
                       "too many periods in floating-point constant");
                 return false;
             }
-        } else if (isxdigit(c)) {
-            int v = hexval(c);
-
+        } else if ((v = hexval(c)) < (unsigned int)radix) {
             if (!seendigit && v) {
                 int l = log2tbl[v];
 
                 seendigit = 1;
                 mp = mult;
-                ms = 15 - l;
+                ms = 15-l;
 
-                twopwr = seendot ? twopwr - 4 + l : l - 3;
+                twopwr = seendot ? twopwr-bits+l : l+1-bits;
             }
 
             if (seendigit) {
@@ -483,13 +485,13 @@ static bool ieee_flconvert_hex(const char *string, uint16_t * mant,
                     ms += 16;
                 }
                 *mp |= v << ms;
-                ms -= 4;
+                ms -= bits;
 
                 if (!seendot)
-                    twopwr += 4;
+                    twopwr += bits;
             } else {
                 if (seendot)
-                    twopwr -= 4;
+                    twopwr -= bits;
             }
         } else if (c == 'p' || c == 'P') {
            int32_t e;
@@ -656,13 +658,41 @@ static int to_float(const char *str, int sign, uint8_t * result,
            break;
         }
     } else {
-        if (str[0] == '0' &&
-           (str[1] == 'x' || str[1] == 'X' || str[1] == 'h' || str[1] == 'H'))
-            ok = ieee_flconvert_hex(str + 2, mant, &exponent);
-       else if (str[0] == '$')
-           ok = ieee_flconvert_hex(str + 1, mant, &exponent);
-        else
+        if (str[0] == '0') {
+           switch (str[1]) {
+           case 'x': case 'X':
+           case 'h': case 'H':
+               ok = ieee_flconvert_bin(str+2, 4, mant, &exponent);
+               break;
+           case 'o': case 'O':
+           case 'q': case 'Q':
+               ok = ieee_flconvert_bin(str+2, 3, mant, &exponent);
+               break;
+           case 'b': case 'B':
+           case 'y': case 'Y':
+               ok = ieee_flconvert_bin(str+2, 1, mant, &exponent);
+               break;
+           case 'd': case 'D':
+           case 't': case 'T':
+               ok = ieee_flconvert(str+2, mant, &exponent);
+               break;
+           case '0': case '1': case '2': case '3': case '4':
+           case '5': case '6': case '7': case '8': case '9':
+           case '\0':
+               /* Leading zero was just a zero */
+               ok = ieee_flconvert(str, mant, &exponent);
+               break;
+           default:
+               error(ERR_NONFATAL,
+                     "floating-point constant: invalid radix `%c'", str[1]);
+               ok = false;
+               break;
+           }
+       } else if (str[0] == '$') {
+           ok = ieee_flconvert_bin(str+1, 4, mant, &exponent);
+       } else {
             ok = ieee_flconvert(str, mant, &exponent);
+       }
 
        if (!ok) {
            type = FL_QNAN;
index dda4e0a..8295054 100644 (file)
--- a/stdscan.c
+++ b/stdscan.c
@@ -141,7 +141,7 @@ int stdscan(void *private_data, struct tokenval *tv)
                }
            } else if (c == 'H' || c == 'h' || c == 'X' || c == 'x') {
                is_hex = true;
-           } else if (is_hex && (c == 'P' || c == 'p')) {
+           } else if (c == 'P' || c == 'p') {
                is_float = true;
                if (*stdscan_bufptr == '+' || *stdscan_bufptr == '-')
                    stdscan_bufptr++;
index 35dbcee..c08b550 100644 (file)
@@ -1,3 +1,5 @@
+       ;;  Integer constants...
+
        dd 1010_0101            ; Decimal
        dd 01010_0101           ; Decimal (*not* octal!)
        dd 0d1010_0101          ; Decimal
        dd 1010_0101h           ; Hex
        dd 1010_0101x           ; Hex
        dd $1010_0101           ; Hex
-       
\ No newline at end of file
+
+       ;; Floating-point constants
+       ;; All of these should output B4A21147
+       dd 3.7282705e+4         ; Decimal
+       dd 00003.7282705e+4     ; Decimal
+       dd 0d3.7282705e+4       ; Decimal
+       dd 0t3.7282705e+4       ; Decimal
+
+       dd 0x1.23456789p+15     ; Hex
+       dd 0h1.23456789p+15     ; Hex
+       
+       dd 0o1.10642547422p+15  ; Octal
+       dd 0q1.10642547422p+15  ; Octal
+
+       dd 0b1.0010_0011_0100_0101_0110_0111_1000_1001p+15 ; Binary
+       dd 0y1.0010_0011_0100_0101_0110_0111_1000_1001p+15 ; Binary