about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/internal/floatscan.c20
1 files changed, 16 insertions, 4 deletions
diff --git a/src/internal/floatscan.c b/src/internal/floatscan.c
index dbe07987..aa962f41 100644
--- a/src/internal/floatscan.c
+++ b/src/internal/floatscan.c
@@ -24,6 +24,8 @@
 
 #define MASK (KMAX-1)
 
+#define CONCAT2(x,y) x ## y
+#define CONCAT(x,y) CONCAT2(x,y)
 
 static long long scanexp(FILE *f, int pok)
 {
@@ -63,6 +65,8 @@ static long double decfloat(FILE *f, int c, int bits, int emin, int sign, int po
 	int gotdig = 0, gotrad = 0;
 	int rp;
 	int e2;
+	int emax = -emin-bits+3;
+	int denormal = 0;
 	long double y;
 	long double frac=0;
 	long double bias=0;
@@ -250,6 +254,7 @@ static long double decfloat(FILE *f, int c, int bits, int emin, int sign, int po
 	if (bits > LDBL_MANT_DIG+e2-emin) {
 		bits = LDBL_MANT_DIG+e2-emin;
 		if (bits<0) bits=0;
+		denormal = 1;
 	}
 
 	/* Calculate bias term to force rounding, move out lower bits */
@@ -280,11 +285,18 @@ static long double decfloat(FILE *f, int c, int bits, int emin, int sign, int po
 	y += frac;
 	y -= bias;
 
-	y = scalbnl(y, e2);
-
-	if (!y) errno = ERANGE;
+	if ((e2+LDBL_MANT_DIG & INT_MAX) > emax-5) {
+		if (fabs(y) >= CONCAT(0x1p, LDBL_MANT_DIG)) {
+			if (denormal && bits==LDBL_MANT_DIG+e2-emin)
+				denormal = 0;
+			y *= 0.5;
+			e2++;
+		}
+		if (e2+LDBL_MANT_DIG>emax || (denormal && frac))
+			errno = ERANGE;
+	}
 
-	return y;
+	return scalbnl(y, e2);
 }
 
 static long double hexfloat(FILE *f, int bits, int emin, int sign, int pok)