mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
e3d28de8a6
When reading hexadecimal floats, cosmopolitan would previously sometimes print a number of warnings relating to undefined behavior on left shift: third_party/gdtoa/gethex.c:172: ubsan warning: signed left shift changed sign bit or overflowed 12 'int' 28 'int' is undefined behavior This is because gdtoa assumes left shifts are safe when overflow happens even on signed integers - this is false: the C standard considers it UB. This is easy to fix, by simply casting the shifted value to unsigned, as doing so does not change the value or the semantics of the left shifting (except for avoiding the undefined behavior, as the C standard specifies that unsigned overflow yields wraparound, avoiding undefined behaviour). This commit does this, and adds a testcase that previously triggered UB. (this also adds test macros to test for exact float equality, instead of the existing {EXPECT,ASSERT}_FLOAT_EQ macros which only tests inputs for being "almost equal" (with a significant epsilon) whereas exact equality makes more sense for certain things such as reading floats from strings, and modifies other testcases for sscanf/fscanf of floats to utilize it).
303 lines
7.9 KiB
C
303 lines
7.9 KiB
C
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
|
|
│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │
|
|
╚──────────────────────────────────────────────────────────────────────────────╝
|
|
│ │
|
|
│ The author of this software is David M. Gay. │
|
|
│ Please send bug reports to David M. Gay <dmg@acm.org> │
|
|
│ or Justine Tunney <jtunney@gmail.com> │
|
|
│ │
|
|
│ Copyright (C) 1998, 1999 by Lucent Technologies │
|
|
│ All Rights Reserved │
|
|
│ │
|
|
│ Permission to use, copy, modify, and distribute this software and │
|
|
│ its documentation for any purpose and without fee is hereby │
|
|
│ granted, provided that the above copyright notice appear in all │
|
|
│ copies and that both that the copyright notice and this │
|
|
│ permission notice and warranty disclaimer appear in supporting │
|
|
│ documentation, and that the name of Lucent or any of its entities │
|
|
│ not be used in advertising or publicity pertaining to │
|
|
│ distribution of the software without specific, written prior │
|
|
│ permission. │
|
|
│ │
|
|
│ LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, │
|
|
│ INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. │
|
|
│ IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY │
|
|
│ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES │
|
|
│ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER │
|
|
│ IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, │
|
|
│ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF │
|
|
│ THIS SOFTWARE. │
|
|
│ │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/errno.h"
|
|
#include "third_party/gdtoa/gdtoa.internal.h"
|
|
|
|
int
|
|
__gdtoa_gethex(const char **sp, const FPI *fpi,
|
|
Long *exp, Bigint **bp, int sign, ThInfo **PTI)
|
|
{
|
|
Bigint *b;
|
|
const unsigned char *decpt, *s0, *s, *s1;
|
|
int big, esign, havedig, irv, j, k, n, n0, nbits, up, zret;
|
|
ULong L, lostbits, *x;
|
|
Long e, e1;
|
|
/**** if (!__gdtoa_hexdig['0']) __gdtoa_hexdig_init(); ****/
|
|
*bp = 0;
|
|
havedig = 0;
|
|
s0 = *(const unsigned char **)sp + 2;
|
|
while(s0[havedig] == '0')
|
|
havedig++;
|
|
s0 += havedig;
|
|
s = s0;
|
|
decpt = 0;
|
|
zret = 0;
|
|
e = 0;
|
|
if (__gdtoa_hexdig[*s])
|
|
havedig++;
|
|
else {
|
|
zret = 1;
|
|
if (*s != '.')
|
|
goto pcheck;
|
|
decpt = ++s;
|
|
if (!__gdtoa_hexdig[*s])
|
|
goto pcheck;
|
|
while(*s == '0')
|
|
s++;
|
|
if (__gdtoa_hexdig[*s])
|
|
zret = 0;
|
|
havedig = 1;
|
|
s0 = s;
|
|
}
|
|
while(__gdtoa_hexdig[*s])
|
|
s++;
|
|
if (*s == '.' && !decpt) {
|
|
decpt = ++s;
|
|
while(__gdtoa_hexdig[*s])
|
|
s++;
|
|
}/*}*/
|
|
if (decpt)
|
|
e = -(((Long)(s-decpt)) << 2);
|
|
pcheck:
|
|
s1 = s;
|
|
big = esign = 0;
|
|
switch(*s) {
|
|
case 'p':
|
|
case 'P':
|
|
switch(*++s) {
|
|
case '-':
|
|
esign = 1;
|
|
/* no break */
|
|
case '+':
|
|
s++;
|
|
}
|
|
if ((n = __gdtoa_hexdig[*s]) == 0 || n > 0x19) {
|
|
s = s1;
|
|
break;
|
|
}
|
|
e1 = n - 0x10;
|
|
while((n = __gdtoa_hexdig[*++s]) !=0 && n <= 0x19) {
|
|
if (e1 & 0xf8000000)
|
|
big = 1;
|
|
e1 = 10*e1 + n - 0x10;
|
|
}
|
|
if (esign)
|
|
e1 = -e1;
|
|
e += e1;
|
|
}
|
|
*sp = (char*)s;
|
|
if (!havedig)
|
|
*sp = (char*)s0 - 1;
|
|
if (zret)
|
|
return STRTOG_Zero;
|
|
if (big) {
|
|
if (esign) {
|
|
switch(fpi->rounding) {
|
|
case FPI_Round_up:
|
|
if (sign)
|
|
break;
|
|
goto ret_tiny;
|
|
case FPI_Round_down:
|
|
if (!sign)
|
|
break;
|
|
goto ret_tiny;
|
|
}
|
|
goto retz;
|
|
ret_tiny:
|
|
b = __gdtoa_Balloc(0, PTI);
|
|
b->wds = 1;
|
|
b->x[0] = 1;
|
|
goto dret;
|
|
}
|
|
switch(fpi->rounding) {
|
|
case FPI_Round_near:
|
|
goto ovfl1;
|
|
case FPI_Round_up:
|
|
if (!sign)
|
|
goto ovfl1;
|
|
goto ret_big;
|
|
case FPI_Round_down:
|
|
if (sign)
|
|
goto ovfl1;
|
|
}
|
|
ret_big:
|
|
nbits = fpi->nbits;
|
|
n0 = n = nbits >> kshift;
|
|
if (nbits & kmask)
|
|
++n;
|
|
for(j = n, k = 0; j >>= 1; ++k);
|
|
*bp = b = __gdtoa_Balloc(k, PTI);
|
|
b->wds = n;
|
|
for(j = 0; j < n0; ++j)
|
|
b->x[j] = ALL_ON;
|
|
if (n > n0)
|
|
b->x[j] = ALL_ON >> (ULbits - (nbits & kmask));
|
|
*exp = fpi->emax;
|
|
return STRTOG_Normal | STRTOG_Inexlo;
|
|
}
|
|
n = s1 - s0 - 1;
|
|
for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1)
|
|
k++;
|
|
b = __gdtoa_Balloc(k, PTI);
|
|
x = b->x;
|
|
n = 0;
|
|
L = 0;
|
|
while(s1 > s0) {
|
|
if (*--s1 == '.')
|
|
continue;
|
|
if (n == ULbits) {
|
|
*x++ = L;
|
|
L = 0;
|
|
n = 0;
|
|
}
|
|
// We can shift in a way that changes the sign bit or overflows,
|
|
// so we need to cast to unsigned to avoid undefined behavior
|
|
L |= (unsigned)(__gdtoa_hexdig[*s1] & 0x0f) << n;
|
|
n += 4;
|
|
}
|
|
*x++ = L;
|
|
b->wds = n = x - b->x;
|
|
n = ULbits*n - hi0bits(L);
|
|
nbits = fpi->nbits;
|
|
lostbits = 0;
|
|
x = b->x;
|
|
if (n > nbits) {
|
|
n -= nbits;
|
|
if (__gdtoa_any_on(b,n)) {
|
|
lostbits = 1;
|
|
k = n - 1;
|
|
if (x[k>>kshift] & 1 << (k & kmask)) {
|
|
lostbits = 2;
|
|
if (k > 0 && __gdtoa_any_on(b,k))
|
|
lostbits = 3;
|
|
}
|
|
}
|
|
__gdtoa_rshift(b, n);
|
|
e += n;
|
|
}
|
|
else if (n < nbits) {
|
|
n = nbits - n;
|
|
b = __gdtoa_lshift(b, n, PTI);
|
|
e -= n;
|
|
x = b->x;
|
|
}
|
|
if (e > fpi->emax) {
|
|
ovfl:
|
|
__gdtoa_Bfree(b, PTI);
|
|
ovfl1:
|
|
errno = ERANGE;
|
|
switch (fpi->rounding) {
|
|
case FPI_Round_zero:
|
|
goto ret_big;
|
|
case FPI_Round_down:
|
|
if (!sign)
|
|
goto ret_big;
|
|
break;
|
|
case FPI_Round_up:
|
|
if (sign)
|
|
goto ret_big;
|
|
}
|
|
return STRTOG_Infinite | STRTOG_Overflow | STRTOG_Inexhi;
|
|
}
|
|
irv = STRTOG_Normal;
|
|
if (e < fpi->emin) {
|
|
irv = STRTOG_Denormal;
|
|
n = fpi->emin - e;
|
|
if (n >= nbits) {
|
|
switch (fpi->rounding) {
|
|
case FPI_Round_near:
|
|
if (n == nbits && (n < 2 || lostbits || __gdtoa_any_on(b,n-1)))
|
|
goto one_bit;
|
|
break;
|
|
case FPI_Round_up:
|
|
if (!sign)
|
|
goto one_bit;
|
|
break;
|
|
case FPI_Round_down:
|
|
if (sign) {
|
|
one_bit:
|
|
x[0] = b->wds = 1;
|
|
dret:
|
|
*bp = b;
|
|
*exp = fpi->emin;
|
|
errno = ERANGE;
|
|
return STRTOG_Denormal | STRTOG_Inexhi
|
|
| STRTOG_Underflow;
|
|
}
|
|
}
|
|
__gdtoa_Bfree(b, PTI);
|
|
retz:
|
|
errno = ERANGE;
|
|
return STRTOG_Zero | STRTOG_Inexlo | STRTOG_Underflow;
|
|
}
|
|
k = n - 1;
|
|
if (lostbits)
|
|
lostbits = 1;
|
|
else if (k > 0)
|
|
lostbits = __gdtoa_any_on(b,k);
|
|
if (x[k>>kshift] & 1 << (k & kmask))
|
|
lostbits |= 2;
|
|
nbits -= n;
|
|
__gdtoa_rshift(b,n);
|
|
e = fpi->emin;
|
|
}
|
|
if (lostbits) {
|
|
up = 0;
|
|
switch(fpi->rounding) {
|
|
case FPI_Round_zero:
|
|
break;
|
|
case FPI_Round_near:
|
|
if (lostbits & 2
|
|
&& (lostbits | x[0]) & 1)
|
|
up = 1;
|
|
break;
|
|
case FPI_Round_up:
|
|
up = 1 - sign;
|
|
break;
|
|
case FPI_Round_down:
|
|
up = sign;
|
|
}
|
|
if (up) {
|
|
k = b->wds;
|
|
b = __gdtoa_increment(b, PTI);
|
|
x = b->x;
|
|
if (irv == STRTOG_Denormal) {
|
|
if (nbits == fpi->nbits - 1
|
|
&& x[nbits >> kshift] & 1 << (nbits & kmask))
|
|
irv = STRTOG_Normal;
|
|
}
|
|
else if (b->wds > k
|
|
|| ((n = nbits & kmask) !=0
|
|
&& hi0bits(x[k-1]) < 32-n)) {
|
|
__gdtoa_rshift(b,1);
|
|
if (++e > fpi->emax)
|
|
goto ovfl;
|
|
}
|
|
irv |= STRTOG_Inexhi;
|
|
}
|
|
else
|
|
irv |= STRTOG_Inexlo;
|
|
}
|
|
*bp = b;
|
|
*exp = e;
|
|
return irv;
|
|
}
|