/* * linux/kernel/math/convert.c * * (C) 1991 Linus Torvalds */ #include <linux/math_emu.h> /* * NOTE!!! There is some "non-obvious" optimisations in the temp_to_long * and temp_to_short conversion routines: don't touch them if you don't * know what's going on. They are the adding of one in the rounding: the * overflow bit is also used for adding one into the exponent. Thus it * looks like the overflow would be incorrectly handled, but due to the * way the IEEE numbers work, things are correct. * * There is no checking for total overflow in the conversions, though (ie * if the temp-real number simply won't fit in a short- or long-real.) */ void short_to_temp(const short_real * a, temp_real * b) { if (!(*a & 0x7fffffff)) { b->a = b->b = 0; if (*a) b->exponent = 0x8000; else b->exponent = 0; return; } b->exponent = ((*a>>23) & 0xff)-127+16383; if (*a<0) b->exponent |= 0x8000; b->b = (*a<<8) | 0x80000000; b->a = 0; } void long_to_temp(const long_real * a, temp_real * b) { if (!a->a && !(a->b & 0x7fffffff)) { b->a = b->b = 0; if (a->b) b->exponent = 0x8000; else b->exponent = 0; return; } b->exponent = ((a->b >> 20) & 0x7ff)-1023+16383; if (a->b<0) b->exponent |= 0x8000; b->b = 0x80000000 | (a->b<<11) | (((unsigned long)a->a)>>21); b->a = a->a<<11; } void temp_to_short(const temp_real * a, short_real * b) { if (!(a->exponent & 0x7fff)) { *b = (a->exponent)?0x80000000:0; return; } *b = ((((long) a->exponent)-16383+127) << 23) & 0x7f800000; if (a->exponent < 0) *b |= 0x80000000; *b |= (a->b >> 8) & 0x007fffff; switch (ROUNDING) { case ROUND_NEAREST: if ((a->b & 0xff) > 0x80) ++*b; break; case ROUND_DOWN: if ((a->exponent & 0x8000) && (a->b & 0xff)) ++*b; break; case ROUND_UP: if (!(a->exponent & 0x8000) && (a->b & 0xff)) ++*b; break; } } void temp_to_long(const temp_real * a, long_real * b) { if (!(a->exponent & 0x7fff)) { b->a = 0; b->b = (a->exponent)?0x80000000:0; return; } b->b = (((0x7fff & (long) a->exponent)-16383+1023) << 20) & 0x7ff00000; if (a->exponent < 0) b->b |= 0x80000000; b->b |= (a->b >> 11) & 0x000fffff; b->a = a->b << 21; b->a |= (a->a >> 11) & 0x001fffff; switch (ROUNDING) { case ROUND_NEAREST: if ((a->a & 0x7ff) > 0x400) __asm__("addl $1,%0 ; adcl $0,%1" :"=r" (b->a),"=r" (b->b) :"0" (b->a),"1" (b->b)); break; case ROUND_DOWN: if ((a->exponent & 0x8000) && (a->b & 0xff)) __asm__("addl $1,%0 ; adcl $0,%1" :"=r" (b->a),"=r" (b->b) :"0" (b->a),"1" (b->b)); break; case ROUND_UP: if (!(a->exponent & 0x8000) && (a->b & 0xff)) __asm__("addl $1,%0 ; adcl $0,%1" :"=r" (b->a),"=r" (b->b) :"0" (b->a),"1" (b->b)); break; } } void frndint(const temp_real * a, temp_real * b) { int shift = 16383 + 63 - (a->exponent & 0x7fff); unsigned long underflow; if ((shift < 0) || (shift == 16383+63)) { *b = *a; return; } b->a = b->b = underflow = 0; b->exponent = a->exponent; if (shift < 32) { b->b = a->b; b->a = a->a; } else if (shift < 64) { b->a = a->b; underflow = a->a; shift -= 32; b->exponent += 32; } else if (shift < 96) { underflow = a->b; shift -= 64; b->exponent += 64; } else { underflow = 1; shift = 0; } b->exponent += shift; __asm__("shrdl %2,%1,%0" :"=r" (underflow),"=r" (b->a) :"c" ((char) shift),"0" (underflow),"1" (b->a)); __asm__("shrdl %2,%1,%0" :"=r" (b->a),"=r" (b->b) :"c" ((char) shift),"0" (b->a),"1" (b->b)); __asm__("shrl %1,%0" :"=r" (b->b) :"c" ((char) shift),"0" (b->b)); switch (ROUNDING) { case ROUND_NEAREST: __asm__("addl %4,%5 ; adcl $0,%0 ; adcl $0,%1" :"=r" (b->a),"=r" (b->b) :"0" (b->a),"1" (b->b) ,"r" (0x7fffffff + (b->a & 1)) ,"m" (*&underflow)); break; case ROUND_UP: if ((b->exponent >= 0) && underflow) __asm__("addl $1,%0 ; adcl $0,%1" :"=r" (b->a),"=r" (b->b) :"0" (b->a),"1" (b->b)); break; case ROUND_DOWN: if ((b->exponent < 0) && underflow) __asm__("addl $1,%0 ; adcl $0,%1" :"=r" (b->a),"=r" (b->b) :"0" (b->a),"1" (b->b)); break; } if (b->a || b->b) while (b->b >= 0) { b->exponent--; __asm__("addl %0,%0 ; adcl %1,%1" :"=r" (b->a),"=r" (b->b) :"0" (b->a),"1" (b->b)); } else b->exponent = 0; } void real_to_int(const temp_real * a, temp_int * b) { int shift = 16383 + 63 - (a->exponent & 0x7fff); unsigned long underflow; b->a = b->b = underflow = 0; b->sign = (a->exponent < 0); if (shift < 0) { set_OE(); return; } if (shift < 32) { b->b = a->b; b->a = a->a; } else if (shift < 64) { b->a = a->b; underflow = a->a; shift -= 32; } else if (shift < 96) { underflow = a->b; shift -= 64; } else { underflow = 1; shift = 0; } __asm__("shrdl %2,%1,%0" :"=r" (underflow),"=r" (b->a) :"c" ((char) shift),"0" (underflow),"1" (b->a)); __asm__("shrdl %2,%1,%0" :"=r" (b->a),"=r" (b->b) :"c" ((char) shift),"0" (b->a),"1" (b->b)); __asm__("shrl %1,%0" :"=r" (b->b) :"c" ((char) shift),"0" (b->b)); switch (ROUNDING) { case ROUND_NEAREST: __asm__("addl %4,%5 ; adcl $0,%0 ; adcl $0,%1" :"=r" (b->a),"=r" (b->b) :"0" (b->a),"1" (b->b) ,"r" (0x7fffffff + (b->a & 1)) ,"m" (*&underflow)); break; case ROUND_UP: if (!b->sign && underflow) __asm__("addl $1,%0 ; adcl $0,%1" :"=r" (b->a),"=r" (b->b) :"0" (b->a),"1" (b->b)); break; case ROUND_DOWN: if (b->sign && underflow) __asm__("addl $1,%0 ; adcl $0,%1" :"=r" (b->a),"=r" (b->b) :"0" (b->a),"1" (b->b)); break; } } void int_to_real(const temp_int * a, temp_real * b) { b->a = a->a; b->b = a->b; if (b->a || b->b) b->exponent = 16383 + 63 + (a->sign? 0x8000:0); else { b->exponent = 0; return; } while (b->b >= 0) { b->exponent--; __asm__("addl %0,%0 ; adcl %1,%1" :"=r" (b->a),"=r" (b->b) :"0" (b->a),"1" (b->b)); } }