Index: libcfa/src/iostream.cfa
===================================================================
--- libcfa/src/iostream.cfa	(revision 4c4e444f4eaf11309c7a94a97b5b9b50be2a54fc)
+++ libcfa/src/iostream.cfa	(revision fd4c009c9dfdc22d84d4bace73fa33c9d4937d90)
@@ -10,6 +10,6 @@
 // Created On       : Wed May 27 17:56:53 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Tue Mar  2 14:51:30 2021
-// Update Count     : 1151
+// Last Modified On : Tue Apr 13 13:05:24 2021
+// Update Count     : 1324
 //
 
@@ -195,8 +195,8 @@
 			int len = snprintf( buf, size, format, ##__VA_ARGS__, val ); \
 			fmt( os, "%s", buf ); \
-			if ( isfinite( val ) ) {					/* if number, always print decimal point */ \
+			if ( isfinite( val ) ) { /* if number, print decimal point when no fraction or exponent */ \
 				for ( int i = 0;; i += 1 ) { \
 					if ( i == len ) { fmt( os, "." ); break; } \
-					if ( buf[i] == '.' ) break; \
+					if ( buf[i] == '.' || buf[i] == 'e' || buf[i] == 'E' ) break; /* decimal point or scientific ? */ \
 				} /* for */ \
 			} /* if */ \
@@ -525,136 +525,16 @@
 } // distribution
 
-IntegralFMTImpl( signed char, "%    *hh ", "%    *.*hh " )
-IntegralFMTImpl( unsigned char, "%    *hh ", "%    *.*hh " )
-IntegralFMTImpl( signed short int, "%    *h ", "%    *.*h " )
-IntegralFMTImpl( unsigned short int, "%    *h ", "%    *.*h " )
-IntegralFMTImpl( signed int, "%    * ", "%    *.* " )
-IntegralFMTImpl( unsigned int, "%    * ", "%    *.* " )
-IntegralFMTImpl( signed long int, "%    *l ", "%    *.*l " )
-IntegralFMTImpl( unsigned long int, "%    *l ", "%    *.*l " )
-IntegralFMTImpl( signed long long int, "%    *ll ", "%    *.*ll " )
-IntegralFMTImpl( unsigned long long int, "%    *ll ", "%    *.*ll " )
-
-#if 0
-#if defined( __SIZEOF_INT128__ )
-// Default prefix for non-decimal prints is 0b, 0, 0x.
-#define IntegralFMTImpl128( T, SIGNED, CODE, IFMTNP, IFMTP ) \
-forall( ostype & | ostream( ostype ) ) \
-static void base10_128( ostype & os, _Ostream_Manip(T) f ) { \
-	if ( f.val > UINT64_MAX ) { \
-		unsigned long long int lsig = f.val % P10_UINT64; \
-		f.val /= P10_UINT64; /* msig */ \
-		base10_128( os, f ); /* recursion */ \
-		_Ostream_Manip(unsigned long long int) fmt @= { lsig, 0, 19, 'u', { .all : 0 } }; \
-		fmt.flags.nobsdp = true; \
-		/* printf( "fmt1 %c %lld %d\n", fmt.base, fmt.val, fmt.all ); */ \
-		sepOff( os ); \
-		(ostype &)(os | fmt); \
-	} else { \
-		/* printf( "fmt2 %c %lld %d\n", f.base, (unsigned long long int)f.val, f.all ); */ \
-		_Ostream_Manip(SIGNED long long int) fmt @= { (SIGNED long long int)f.val, f.wd, f.pc, f.base, { .all : f.all } }; \
-		(ostype &)(os | fmt); \
-	} /* if */ \
-} /* base10_128 */ \
-forall( ostype & | ostream( ostype ) ) { \
-	ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \
-		if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); \
-\
-		if ( f.base == 'b' | f.base == 'B' | f.base == 'o' | f.base == 'x' | f.base == 'X' ) { \
-			unsigned long long int msig = (unsigned long long int)(f.val >> 64); \
-			unsigned long long int lsig = (unsigned long long int)(f.val); \
-			_Ostream_Manip(SIGNED long long int) fmt @= { msig, f.wd, f.pc, f.base, { .all : f.all } }; \
-			_Ostream_Manip(unsigned long long int) fmt2 @= { lsig, 0, 0, f.base, { .all : 0 } }; \
-			if ( msig == 0 ) { \
-				fmt.val = lsig; \
-				(ostype &)(os | fmt); \
-			} else { \
-				fmt2.flags.pad0 = fmt2.flags.nobsdp = true;	\
-				if ( f.base == 'b' | f.base == 'B' ) { \
-					if ( fmt.flags.pc && fmt.pc > 64 ) fmt.pc -= 64; else { fmt.flags.pc = false; fmt.pc = 0; } \
-					if ( fmt.flags.left ) { \
-						fmt.flags.left = false; \
-						fmt.wd = 0; \
-						/* printf( "L %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \
-						fmt2.flags.left = true;	\
-						int msigd = high1( msig ); \
-						fmt2.wd = f.wd - (fmt.pc > msigd ? fmt.pc : msigd); \
-						if ( ! fmt.flags.nobsdp ) fmt2.wd -= 2; /* compensate for 0b base specifier */ \
-						if ( (int)fmt2.wd < 64 ) fmt2.wd = 64; /* cast deals with negative value */ \
-						fmt2.flags.pc = true; fmt2.pc = 64; \
-					} else { \
-						if ( fmt.wd > 64 ) fmt.wd -= 64; \
-						else fmt.wd = 1; \
-						/* printf( "R %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \
-						fmt2.wd = 64; \
-					} /* if */ \
-					/* printf( "C %llo %d %d '%c' %x\n", fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \
-					(ostype &)(os | fmt | "" | fmt2); \
-				} else if ( f.base == 'o' ) { \
-					if ( fmt.flags.pc && fmt.pc > 22 ) fmt.pc -= 22; else { fmt.flags.pc = false; fmt.pc = 0; } \
-					fmt.val = (unsigned long long int)fmt.val >> 2; \
-					fmt2.val = ((msig & 0x3) << 1) + ((lsig & 0x8000000000000000U) != 0); \
-					if ( fmt.flags.left ) { \
-						fmt.flags.left = false; \
-						fmt.wd = 0; \
-						/* printf( "L %llo %llo %llo %d %d '%c' %x %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all, fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \
-						(ostype &)(os | fmt | "" | fmt2); \
-						sepOff( os ); \
-						fmt2.flags.left = true;	\
-						int msigd = ceiling_div( high1( fmt.val ), 3 ); \
-						fmt2.wd = f.wd - (fmt.pc > msigd ? fmt.pc : msigd); \
-						if ( ! fmt.flags.nobsdp ) fmt2.wd -= 1; /* compensate for 0 base specifier */ \
-						if ( (int)fmt2.wd < 21 ) fmt2.wd = 21; /* cast deals with negative value */ \
-						fmt2.flags.pc = true; fmt2.pc = 21; \
-					} else { \
-						if ( fmt.wd > 22 ) fmt.wd -= 22; \
-						else fmt.wd = 1; \
-						/* printf( "R %llo %llo %llo %d %d '%c' %x %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all, fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \
-						(ostype &)(os | fmt | "" | fmt2); \
-						sepOff( os ); \
-						fmt2.wd = 21; \
-					} /* if */ \
-					fmt2.val = lsig & 0x7fffffffffffffffU; \
-					/* printf( "\nC %llo %d %d '%c' %x\n", fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \
-					(ostype &)(os | fmt2); \
-				} else { /* f.base == 'x'  | f.base == 'X' */ \
-					if ( fmt.flags.pc && fmt.pc > 16 ) fmt.pc -= 16; else { fmt.flags.pc = false; fmt.pc = 0; } \
-					if ( fmt.flags.left ) { \
-						fmt.flags.left = false; \
-						fmt.wd = 0; \
-						/* printf( "L %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \
-						fmt2.flags.left = true;	\
-						int msigd = high1( msig ); \
-						fmt2.wd = f.wd - (fmt.pc > msigd ? fmt.pc : msigd); \
-						if ( ! fmt.flags.nobsdp ) fmt2.wd -= 2; /* compensate for 0x base specifier */ \
-						if ( (int)fmt2.wd < 16 ) fmt2.wd = 16; /* cast deals with negative value */ \
-						fmt2.flags.pc = true; fmt2.pc = 16; \
-					} else { \
-						if ( fmt.wd > 16 ) fmt.wd -= 16; \
-						else fmt.wd = 1; \
-						/* printf( "R %llo %llo %llo %d %d '%c' %x\n", msig, lsig, fmt.val, fmt.wd, fmt.pc, fmt.base, fmt.all ); */ \
-						fmt2.wd = 16; \
-					} /* if */ \
-					/* printf( "C %llo %d %d '%c' %x\n", fmt2.val, fmt2.wd, fmt2.pc, fmt2.base, fmt2.all ); */ \
-					(ostype &)(os | fmt | "" | fmt2); \
-				} /* if */ \
-			} /* if */ \
-		} else { \
-			if ( CODE == 'd' ) { \
-				if ( f.val < 0 )  { fmt( os, "-" ); sepOff( os ); f.val = -f.val; f.flags.sign = false; } \
-			} /* if */ \
-			base10_128( os, f ); \
-		} /* if */ \
-		return os; \
-	} /* ?|? */ \
-	void ?|?( ostype & os, _Ostream_Manip(T) f ) { (ostype &)(os | f); ends( os ); } \
-} // distribution
-
-IntegralFMTImpl128( int128, signed, 'd', "%    *ll ", "%    *.*ll " )
-IntegralFMTImpl128( unsigned int128, unsigned, 'u', "%    *ll ", "%    *.*ll " )
-#endif // __SIZEOF_INT128__
-#endif // 0
-
-#if 1
+IntegralFMTImpl( signed char, "     *hh ", "     *.*hh " )
+IntegralFMTImpl( unsigned char, "     *hh ", "     *.*hh " )
+IntegralFMTImpl( signed short int, "     *h ", "     *.*h " )
+IntegralFMTImpl( unsigned short int, "     *h ", "     *.*h " )
+IntegralFMTImpl( signed int, "     * ", "     *.* " )
+IntegralFMTImpl( unsigned int, "     * ", "     *.* " )
+IntegralFMTImpl( signed long int, "     *l ", "     *.*l " )
+IntegralFMTImpl( unsigned long int, "     *l ", "     *.*l " )
+IntegralFMTImpl( signed long long int, "     *ll ", "     *.*ll " )
+IntegralFMTImpl( unsigned long long int, "     *ll ", "     *.*ll " )
+
+
 #if defined( __SIZEOF_INT128__ )
 // Default prefix for non-decimal prints is 0b, 0, 0x.
@@ -746,25 +626,50 @@
 IntegralFMTImpl128( unsigned int128 )
 #endif // __SIZEOF_INT128__
-#endif // 0
 
 // *********************************** floating point ***********************************
 
-#define PrintWithDP2( os, format, val, ... ) \
+static const char *suffixes[] = {
+	"y", "z", "a", "f", "p", "n", "u", "m", "",
+	"K", "M", "G", "T", "P", "E", "Z", "Y"
+}; 
+#define SUFFIXES_START (-24) /* Smallest power for which there is a suffix defined. */
+#define SUFFIXES_END (SUFFIXES_START + (int)((sizeof(suffixes) / sizeof(char *) - 1) * 3))
+
+#define PrintWithDP2( os, format, ... ) \
 	{ \
-		enum { size = 48 }; \
-		char buf[size]; \
-		int bufbeg = 0, i, len = snprintf( buf, size, format, ##__VA_ARGS__, val ); \
-		if ( isfinite( val ) && (f.base != 'g' || f.pc != 0) ) { /* if number, print decimal point */ \
-			for ( i = 0; i < len && buf[i] != '.' && buf[i] != 'e' && buf[i] != 'E'; i += 1 ); /* decimal point or scientific ? */ \
-			if ( i == len && ! f.flags.nobsdp ) { \
-				if ( ! f.flags.left ) { \
-					buf[i] = '.'; buf[i + 1] = '\0'; \
-					if ( buf[0] == ' ' ) bufbeg = 1;	/* decimal point within width */ \
-				} else { \
-					for ( i = 0; i < len && buf[i] != ' '; i += 1 ); /* trailing blank ? */ \
-					buf[i] = '.'; \
-					if ( i == len ) buf[i + 1] = '\0'; \
+		if ( ! f.flags.eng ) { \
+			len = snprintf( buf, size, format, ##__VA_ARGS__ ); \
+			if ( isfinite( f.val ) && ( f.pc != 0 || ! f.flags.nobsdp ) ) { /* if number, print decimal point when no fraction or exponent */ \
+				for ( i = 0; i < len && buf[i] != '.' && buf[i] != 'e' && buf[i] != 'E'; i += 1 ); /* decimal point or scientific ? */ \
+				if ( i == len ) { \
+					if ( ! f.flags.left ) { \
+						buf[i] = '.'; buf[i + 1] = '\0'; \
+						if ( buf[0] == ' ' ) bufbeg = 1; /* decimal point within width */ \
+					} else { \
+						for ( i = 0; i < len && buf[i] != ' '; i += 1 ); /* trailing blank ? */ \
+						buf[i] = '.'; \
+						if ( i == len ) buf[i + 1] = '\0'; \
+					} /* if */ \
 				} /* if */ \
 			} /* if */ \
+		} else { \
+			int exp10, len2; \
+			eng( f.val, f.pc, exp10 );					/* changes arguments */ \
+			if ( ! f.flags.left && f.wd > 1 ) { \
+				/* Exponent size (number of digits, 'e', optional minus sign) */ \
+				f.wd -= lrint( floor( log10( abs( exp10 ) ) ) ) + 1 + 1 + (exp10 < 0 ? 1 : 0); \
+				if ( f.wd < 1 ) f.wd = 1; \
+			} /* if */ \
+			len = snprintf( buf, size, format, ##__VA_ARGS__ ); \
+			if ( f.flags.left ) { \
+				for ( len -= 1; len > 0 && buf[len] == ' '; len -= 1 ); \
+				len += 1; \
+			} /* if */ \
+			if ( ! f.flags.nobsdp || (exp10 < SUFFIXES_START) || (exp10 > SUFFIXES_END) ) { \
+				len2 = snprintf( &buf[len], size - len, "e%d", exp10 ); \
+			} else { \
+				len2 = snprintf( &buf[len], size - len, "%s", suffixes[(exp10 - SUFFIXES_START) / 3] ); \
+			} /* if */ \
+			if ( f.flags.left && len + len2 < f.wd ) buf[len + len2] = ' '; \
 		} /* if */ \
 		fmt( os, "%s", &buf[bufbeg] ); \
@@ -773,7 +678,19 @@
 #define FloatingPointFMTImpl( T, DFMTNP, DFMTP ) \
 forall( ostype & | ostream( ostype ) ) { \
+	static void eng( T &value, int & pc, int & exp10 ) { \
+		exp10 = lrint( floor( log10( abs( value ) ) ) ); /* round to desired precision */ \
+		if ( exp10 < 0 ) exp10 -= 2; \
+		exp10 = floor( exp10, 3 ); \
+		value *= pow( 10.0, -exp10 ); \
+		if ( pc <= 3 ) pc = 3; \
+	} /* eng */ \
+\
 	ostype & ?|?( ostype & os, _Ostream_Manip(T) f ) { \
+		enum { size = 48 }; \
+		char buf[size]; \
+		int bufbeg = 0, i, len; \
+\
 		if ( $sepPrt( os ) ) fmt( os, "%s", $sepGetCur( os ) ); \
-		char fmtstr[sizeof(DFMTP)];						/* sizeof includes '\0' */ \
+		char fmtstr[sizeof(DFMTP) + 8];					/* sizeof includes '\0' */ \
 		if ( ! f.flags.pc ) memcpy( &fmtstr, DFMTNP, sizeof(DFMTNP) ); \
 		else memcpy( &fmtstr, DFMTP, sizeof(DFMTP) ); \
@@ -789,9 +706,9 @@
 			fmtstr[sizeof(DFMTNP)-2] = f.base;			/* sizeof includes '\0' */ \
 			/* printf( "%g %d %s\n", f.val, f.wd, &fmtstr[star]); */ \
-			PrintWithDP2( os, &fmtstr[star], f.val, f.wd ) \
+			PrintWithDP2( os, &fmtstr[star], f.wd, f.val ) \
 		} else {										/* precision */ \
 			fmtstr[sizeof(DFMTP)-2] = f.base;			/* sizeof includes '\0' */ \
 			/* printf( "%g %d %d %s\n", f.val, f.wd, f.pc, &fmtstr[star] ); */ \
-			PrintWithDP2( os, &fmtstr[star], f.val, f.wd, f.pc ) \
+			PrintWithDP2( os, &fmtstr[star], f.wd, f.pc, f.val ) \
 		} /* if */ \
 		return os; \
@@ -801,6 +718,6 @@
 } // distribution
 
-FloatingPointFMTImpl( double, "%    * ", "%    *.* " )
-FloatingPointFMTImpl( long double, "%    *L ", "%    *.*L " )
+FloatingPointFMTImpl( double, "     * ", "     *.* " )
+FloatingPointFMTImpl( long double, "     *L ", "     *.*L " )
 
 // *********************************** character ***********************************
Index: libcfa/src/iostream.hfa
===================================================================
--- libcfa/src/iostream.hfa	(revision 4c4e444f4eaf11309c7a94a97b5b9b50be2a54fc)
+++ libcfa/src/iostream.hfa	(revision fd4c009c9dfdc22d84d4bace73fa33c9d4937d90)
@@ -10,6 +10,6 @@
 // Created On       : Wed May 27 17:56:53 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Tue Mar  2 14:05:08 2021
-// Update Count     : 369
+// Last Modified On : Tue Apr 13 13:05:11 2021
+// Update Count     : 384
 //
 
@@ -158,9 +158,10 @@
 struct _Ostream_Manip {
 	T val;												// polymorphic base-type
-	unsigned int wd, pc;								// width, precision
+	int wd, pc;											// width, precision
 	char base;											// numeric base / floating-point style
 	union {
 		unsigned char all;
 		struct {
+			unsigned char eng:1;						// engineering notation
 			unsigned char neg:1;						// val is negative
 			unsigned char pc:1;							// precision specified
@@ -222,9 +223,11 @@
 	_Ostream_Manip(T) hex( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'a', { .all : 0 } }; } \
 	_Ostream_Manip(T) sci( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'e', { .all : 0 } }; } \
-	_Ostream_Manip(T) wd( unsigned int w, T val ) { return (_Ostream_Manip(T))@{ val, w, 0, 'f', { .all : 0 } }; } \
+	_Ostream_Manip(T) eng( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'g', { .flags.eng : true } }; } \
+	_Ostream_Manip(T) wd( unsigned int w, T val ) { return (_Ostream_Manip(T))@{ val, w, 0, 'g', { .all : 0 } }; } \
 	_Ostream_Manip(T) wd( unsigned int w, unsigned char pc, T val ) { return (_Ostream_Manip(T))@{ val, w, pc, 'f', { .flags.pc : true } }; } \
 	_Ostream_Manip(T) ws( unsigned int w, unsigned char pc, T val ) { return (_Ostream_Manip(T))@{ val, w, pc, 'g', { .flags.pc : true } }; } \
-	_Ostream_Manip(T) & wd( unsigned int w, _Ostream_Manip(T) & fmt ) { fmt.wd = w; return fmt; } \
-	_Ostream_Manip(T) & wd( unsigned int w, unsigned char pc, _Ostream_Manip(T) & fmt ) { fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \
+	_Ostream_Manip(T) & wd( unsigned int w, _Ostream_Manip(T) & fmt ) { if ( fmt.flags.eng ) fmt.base = 'f'; fmt.wd = w; return fmt; } \
+	_Ostream_Manip(T) & wd( unsigned int w, unsigned char pc, _Ostream_Manip(T) & fmt ) { if ( fmt.flags.eng ) fmt.base = 'f'; fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \
+	_Ostream_Manip(T) & ws( unsigned int w, unsigned char pc, _Ostream_Manip(T) & fmt ) { fmt.wd = w; fmt.pc = pc; fmt.flags.pc = true; return fmt; } \
 	_Ostream_Manip(T) & left( _Ostream_Manip(T) & fmt ) { fmt.flags.left = true; return fmt; } \
 	_Ostream_Manip(T) upcase( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'G', { .all : 0 } }; } \
@@ -235,4 +238,6 @@
 	_Ostream_Manip(T) nodp( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'g', { .flags.nobsdp : true } }; } \
 	_Ostream_Manip(T) & nodp( _Ostream_Manip(T) & fmt ) { fmt.flags.nobsdp = true; return fmt; } \
+	_Ostream_Manip(T) unit( T val ) { return (_Ostream_Manip(T))@{ val, 1, 0, 'g', { .flags.nobsdp : true } }; } \
+	_Ostream_Manip(T) & unit( _Ostream_Manip(T) & fmt ) { fmt.flags.nobsdp = true; return fmt; } \
 } /* distribution */ \
 forall( ostype & | ostream( ostype ) ) { \
Index: tests/vector_math/.expect/vec4_float.txt
===================================================================
--- tests/vector_math/.expect/vec4_float.txt	(revision 4c4e444f4eaf11309c7a94a97b5b9b50be2a54fc)
+++ tests/vector_math/.expect/vec4_float.txt	(revision fd4c009c9dfdc22d84d4bace73fa33c9d4937d90)
@@ -6,7 +6,7 @@
 zero-assign:<0.,0.,0.,0.>
 fill-ctor:<1.23,1.23,1.23,1.23>
-?-?:<0.02,0.43,-0.999998,-1e-06.>
-?-=?:<0.02,0.43,-0.999998,-1e-06.>
--?:<-0.02,-0.43,0.999998,1e-06.>
+?-?:<0.02,0.43,-0.999998,-1e-06>
+?-=?:<0.02,0.43,-0.999998,-1e-06>
+-?:<-0.02,-0.43,0.999998,1e-06>
 ?+?:<2.3,2.45,-9.2,-12.5>
 ?+=?:<2.3,2.45,-9.2,-12.5>
