Thread: Probably got this wrong

  1. #1
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733

    Probably got this wrong

    Trying to write a function that will convert a long double to a string, here's what I have so far:
    Code:
    typedef signed char db;
    typedef unsigned char ub;
    typedef signed short dw;
    typedef unsigned short uw;
    typedef signed int di;
    typedef unsigned int ui;
    typedef signed long dl;
    typedef unsigned long ul;
    typedef signed long long dq;
    typedef unsigned long long uq;
    typedef float flt;
    typedef double dbl;
    typedef long double ldb;
    
    #if INT_MAX == SHRT_MAX
    #ifdef __SILP64__
    typedef int32_t dd;
    typedef uint32_t ud;
    #define DD_MAX INT32_MAX
    #define DD_MIN INT32_MIN
    #define UD_MAX UINT32_MAX
    #else
    typedef signed long dd;
    typedef unsigned long ud;
    #define DD_MAX LONG_MAX
    #define DD_MIN LONG_MIN
    #define UD_MAX ULONG_MAX
    #endif
    #elseif INT_MAX < LONG_MAX
    typedef signed int dd;
    typedef unsigned int ud;
    #define DD_MAX INT_MAX
    #define DD_MIN INT_MIN
    #define UD_MAX UINT_MAX
    #else
    typedef int32_t dd;
    typedef uint32_t ud;
    #define DD_MAX INT32_MAX
    #define DD_MIN INT32_MIN
    #define UD_MAX UINT32_MAX
    #endif
    ...
    int string_from_ld(
    	string_t *dst,
    	_Bool use_lower_base,
    	_Bool withE,
    	size_t base,
    	register long double val ) {
    	register size_t i, j, cap;
    	register char c;
    	register char const *base_txt = use_lower_base ? l_lower_base : l_upper_base;
    	int ret;
    	string_t string;
    	ub neg = 0;
    	ub exp_neg_bit = (ub)SCHAR_MIN >> 1;
    	ud exp = 0;
    	ud dig = CHAR_BIT - 1;
    	uq man = 0;
    	union {
    		long double f;
    		ub b[sizeof(long double)];
    	};
    	f = val;
    	i = sizeof(long double) - 1;
    	neg = b[i] & SCHAR_MIN & exp_neg_bit;
    	if ( neg )
    		b[i] ^= neg;
    	exp = b[i];
    	while ( i && dig < LDBL_DIG ) {
    		exp <<= CHAR_BIT;
    		exp |= b[--i];
    		dig += CHAR_BIT;
    	}
    	while ( dig > LDBL_DIG ) {
    		man <<=  1;
    		man |= exp & 1;
    		exp >>= 1;
    		--dig;
    	}
    	if ( neg & exp_neg_bit ) {
    		neg ^= exp_neg_bit;
    		exp |= DD_MIN;
    	}
    	while ( i-- ) {
    		man <<= CHAR_BIT;
    		man |= b[i];
    	}
    	string = *dst;
    	if ( neg ) {
    		string->block[0] = '-';
    		string->block++;
    		string->total--;
    		string->bytes--;
    	}
    	ret = string_from_uq( &string, use_lower_base, base, man );
    	if ( neg ) {
    		string->block--;
    		string->total++;
    		string->bytes++;
    		string->count++;
    	}
    	i = string.count + 1;
    	if ( i == string.total && ret != 0 )
    		ret = ENOMEM;
    	if ( ret != 0 ) {
    		*dst = string;
    		return ret;
    	}
    	if ( withE ) {
    		string.block[i-1] = ((dd)exp < 0) ? '-' : '+';
    		string.block = string.block[i];
    		string.total -= i;
    		string.bytes -= i;
    		ret = string_from_dq( &string, use_lower_base, base, (dd)exp );
    		string.count += i;
    		string.total += i;
    		string.bytes += i;
    		string.block = dst->block;
    		*dst = string;
    		return ret;
    	}
    	if ( (dd)exp < 0 ) {
    		exp = -((dd)exp);
    		j = i + (exp += 1);
    		string.count = i;
    		if ( exp <= man ) {
    			j = i + 1;
    			while ( i ) {
    				string.block[j--] = string.block[i--];
    			}
    			string.block[1] = '.';
    		}
    		if ( j < string.total ) {
    			while ( i ) {
    				string.block[j--] = string.block[i--];
    			}
    			j = 0;
    			i = string.count;
    			while ( exp-- > LDBL_MANT_DIG ) {
    				string.block[j++] = '0'; ++i;
    			}
    			string.block[1] = '.';
    		}
    	}
    	else {
    		while ( exp-- > LDBL_MANT_DIG ) {
    			if ( i == string.total ) {
    				ret = ENOMEM;
    				--i;
    				break;
    			}
    			string.block[i++] = '0';
    		}
    	}
    	string.block[i] = 0;
    	string.count = i;
    	*dst = string;
    	return ret;
    }
    I rewrote this function once already so don't mind any unused variables that happen to be left over, haven't attempted to compile this yet since I've got a strong feeling I'm handling this wrong

  2. #2
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    Maybe this helps: Priting Floating Point Numbers Accurately.
    Another thing: the register keyword don't do what you think it does...

  3. #3
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    Thanks for the link, read some of it but need to brush up on my math symbols for the other part.
    For now I will put aside the mantissa and ask if I'm handling the exponent correctly:
    Code:
    typedef union ldb_union {
    	ldb f;
    	ub b[sizeof(ldb)];
    } ldb_union_t;
    int string_from_ldb(
    	string_t *dst,
    	_Bool lcase,
    	_Bool withE,
    	size_t base,
    	register ldb val ) {
    	register size_t i, j, cap;
    	register char c;
    	register char const *base_txt = lcase ? l_lower_base : l_upper_base;
    	int ret = string_chk( dst );
    	string_t string;
    	ub neg = 0;
    	ub exp_neg_bit = (ub)SCHAR_MIN >> 1;
    	ud exp = 0;
    	ud dig = CHAR_BIT - 1;
    	uq man = 0;
    	ldb_union_t fpn = {val};
    	if ( ret != 0 )
    		return ret;
    	i = sizeof(ldb) - 1;
    	neg = fpn.b[i] & SCHAR_MIN & exp_neg_bit;
    	if ( neg )
    		fpn.b[i] ^= neg;
    	exp = fpn.b[i];
    	while ( i && dig < LDBL_DIG ) {
    		exp <<= CHAR_BIT;
    		exp |= fpn.b[--i];
    		dig += CHAR_BIT;
    	}
    	while ( dig > LDBL_DIG ) {
    		man <<=  1;
    		man |= exp & 1;
    		exp >>= 1;
    		--dig;
    	}
    	if ( neg & exp_neg_bit ) {
    		neg ^= exp_neg_bit;
    		exp |= DD_MIN;
    	}
    	while ( i-- ) {
    		man <<= CHAR_BIT;
    		man |= fpn.b[i];
    	}
    	if ( neg ) {
    		dst->block[0] = '-';
    		string = string_pos( dst, 1, dst->count - 1 );
    	}
    	else string = *dst;
    	ret = string_from_uq( &string, lcase, base, man );
    	dst->count = string.count + (neg != 0);
    	i = dst->count + 1;
    	if ( i == dst->total && ret != 0 )
    		ret = ENOMEM;
    	if ( ret != 0 )
    		return ret;
    	/* Process Exponent */
    	if ( withE ) {
    		dst->block[i-1] = ((dd)exp < 0) ? '-' : '+';
    		string = string_pos( dst, i, 0 );
    		ret = string_from_dq( &string, lcase, base, (dd)exp );
    		dst->count += string.count;
    		return ret;
    	}
    	if ( (dd)exp < 0 ) {
    		exp = -((dd)exp);
    		if ( exp < dst->count )
    			return 0;
    		i = exp + 1;
    		dst->count += i;
    		if ( dst->count >= dst->total ) {
    			dst->count -= i;
    			return ENOMEM;
    		}
    		(void)memmove( &dst->block[i], dst->block, dst->count );
    		dst->block[0] = '0';
    		dst->block[1] = '.';
    		for ( i = 0, --exp; i < exp; ++i ) {
    			dst->block[i] = '0';
    		}
    		return 0;
    	}
    	while ( exp-- ) {
    		if ( i == dst->total ) { ret = ENOMEM; --i; break; }
    		dst->block[i++] = '0';
    	}
    	if ( !strncat( dst->block, ".0", dst->total ) )
    		ret = ENOMEM;
    	else i += 2;
    	dst->count = i;
    	return ret;
    }

  4. #4
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    Sorry... I'm lazy enough not to read an ill formated and uncommented code...

  5. #5
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    There is A comment, at the point I start processing the exponent, as for ill formed what do you mean?

  6. #6
    TEIAM - problem solved
    Join Date
    Apr 2012
    Location
    Melbourne Australia
    Posts
    1,907
    Where is this code being ran?
    Remember that reading a member of a union whose value was entered from a different member is implementation defined

  7. #7
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    For now it's being run on a little endian system (linux mint x64), before worrying about the various endians to retieve information from I'm focusing on what to do with the information once I have it, I can then focus on correcting any mistakes in retrieval and debug the output at that time to see if the value is being printed correctly or rather the same as snprintf() would, reason I don't rely on snprintf() is because it's not always defined which is annoying to deal with so just making my own variant so I can guarantee my compilation code will not segfault due to incorrect assumption that the provided string is big enough.

    integers were pretty easy it's just the floating point numbers that I'm struggling with (one more reason to be impressed with the peops that developed whatever handler is used by sprintf and co, I'm tempted to just create a stack buffer like this:
    Code:
    char fpn_txt[(sizeof(ldb) * CHAR_BIT)+2] = {0};
    and then just copy that into the destination buffer

  8. #8
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,733
    For now I stuck this in:
    Code:
    if ( CHEAT_ON_PRINT ) {
    	if ( withE )
    		(void)sprintf(fpn_txt, "Le", val);
    	else
    		(void)sprintf(fpn_txt, "lf", val);
    	dst->count = (sizeof(ldb) * CHAR_BIT) + 2;
    	if ( dst->count >= dst->total )
    		dst->count = dst->total - 1;
    	memcpy( dst->block, fpn_txt, dst->count );
    	dst->block[dst->count] = 0;
    	return 0;
    }
    But I will read some of that pseudo code in the link to try and see how I should handle the mantissa and exponent

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Why so wrong numbers? As I write so wrong?
    By Dmy in forum C++ Programming
    Replies: 2
    Last Post: 07-31-2017, 02:10 PM
  2. Replies: 3
    Last Post: 11-14-2011, 06:35 PM
  3. wrong wrong with my xor function?
    By Anddos in forum C++ Programming
    Replies: 5
    Last Post: 04-26-2009, 01:38 PM
  4. whats wrong with this? no errors but wrong result
    By InvariantLoop in forum C Programming
    Replies: 6
    Last Post: 01-28-2005, 12:48 AM
  5. Replies: 9
    Last Post: 07-15-2004, 03:30 PM

Tags for this Thread