diff --git a/goffice/utils/go-format.c b/goffice/utils/go-format.c
index cf45676..fc30684 100644
--- a/goffice/utils/go-format.c
+++ b/goffice/utils/go-format.c
@@ -55,6 +55,7 @@
 #define ALLOW_PI_SLASH
 #define ALLOW_NEGATIVE_TIMES
 #define MAX_DECIMALS 100
+#define ALLOW_SI_FORMAT
 
 /* ------------------------------------------------------------------------- */
 
@@ -178,7 +179,10 @@ typedef enum {
 #ifdef ALLOW_EE_MARKUP
 	OP_NUM_MARK_MANTISSA,
 	OP_NUM_SIMPLIFY_MANTISSA,
-	OP_NUM_SIMPLIFY_EXPONENT,
+	OP_NUM_SIMPLIFY_MARKUP_EXPONENT,
+#endif
+#ifdef ALLOW_SI_FORMAT
+	OP_NUM_SI_EXPONENT,
 #endif
 	OP_NUM_FRACTION,	/* wholep explicitp (digits|denominator) */
 	OP_NUM_FRACTION_WHOLE,
@@ -288,6 +292,7 @@ struct _GOFormat {
 			guchar *program;
 			unsigned int E_format    : 1;
 			unsigned int use_markup  : 1;
+			unsigned int use_SI      : 1;
 			unsigned int has_date    : 1;
 			unsigned int date_ybm    : 1;  /* year, then month.  */
 			unsigned int date_mbd    : 1;  /* month, then day.  */
@@ -361,6 +366,16 @@ typedef struct {
 #define UNICODE_YEN 0x00a5
 #define UNICODE_YEN_WIDE 0xffe5
 
+#ifdef ALLOW_SI_FORMAT
+/* SI prefixes of powers of three.  (Thus ignoring h, "da", "d", and "c".)  */
+static const char *const
+si_prefixes[] = {
+	"Y", "Z", "E", "P", "T", "G", "M", "k",  /* 24, 21, ..., 3 */
+	"",
+	"m", "\xCE\xBC", "n", "p", "f", "a", "z", "y"  /* -3, -6, ..., -24 */
+};
+#endif
+
 GOFormatFamily
 go_format_get_family (GOFormat const *fmt)
 {
@@ -1054,10 +1069,14 @@ go_format_preparse (const char *str, GOFormatParseState *pstate,
 		case 'E':
 			ntokens++;
 			if (pstate->tno_E >= 0) {
-#ifdef ALLOW_EE_MARKUP
+#if defined(ALLOW_EE_MARKUP) || defined(ALLOW_SI_FORMAT)
 				if (pstate->tno_E == tno - 1)
 					break;
 #endif
+#ifdef ALLOW_SI_FORMAT
+				if (pstate->tno_E == tno - 2)
+					break;
+#endif
 				goto error;
 			}
 			pstate->tno_E = tno;
@@ -1564,10 +1583,15 @@ go_format_parse_number_new_1 (GString *prg, GOFormatParseState *pstate,
 			break;
 
 		case '%':
-			if (E_part != 2)
+			if (E_part != 1 && E_part != 2)
 				scale += 2;
 			break;
 
+		case '\'':
+			if (E_part == 1)
+				scale += 3;
+			break;
+
 		case TOK_THOUSAND:
 			if (tno_numstart != -1 && E_part != 2) {
 				if (comma_is_thousands (ti->tstr)) {
@@ -1591,6 +1615,7 @@ go_format_parse_number_new_1 (GString *prg, GOFormatParseState *pstate,
 		if (tno_numstart == -1)
 			goto error;
 
+		ADD_OP2 (OP_NUM_SCALE, scale);
 		ADD_OP3 (OP_NUM_PRINTF_E, decimals, whole_digits);
 #ifdef OBSERVE_XL_EXPONENT_1
 		if (whole_digits + decimals == 0) {
@@ -1602,7 +1627,7 @@ go_format_parse_number_new_1 (GString *prg, GOFormatParseState *pstate,
 		}
 #endif
 	} else {
-		if (scale && !frac_part)
+		if (scale && !frac_part && E_part != 2)
 			ADD_OP2 (OP_NUM_SCALE, scale);
 		ADD_OP2 (OP_NUM_PRINTF_F, decimals);
 		if (thousands && !inhibit_thousands)
@@ -1653,6 +1678,10 @@ go_format_parse_number_new_1 (GString *prg, GOFormatParseState *pstate,
 			if (frac_part == 3)
 				ADD_OP2 (OP_CHAR, ',');
 			break;
+		case '\'':
+			if (E_part == 1)
+				break;
+			/* Fall through */
 		default:
 			handle_common_token (ti->tstr, ti->token, prg);
 		}
@@ -1714,7 +1743,8 @@ go_format_parse_number_E (GOFormatParseState *pstate)
 {
 	GOFormat *fmt;
 	GString *prg;
-	gboolean use_markup;
+	gboolean use_markup = FALSE;
+	gboolean use_SI = FALSE;
 	int tno_end = pstate->tokens->len;
 	int tno_exp_start = pstate->tno_E + 2;
 	gboolean simplify_mantissa;
@@ -1723,38 +1753,44 @@ go_format_parse_number_E (GOFormatParseState *pstate)
 		return NULL;
 	switch (GET_TOKEN (pstate->tno_E + 1).token) {
 	case '-':
-		use_markup = FALSE;
 		simplify_mantissa = FALSE;
 		pstate->forced_exponent_sign = FALSE;
 		break;
 	case '+':
-		use_markup = FALSE;
 		simplify_mantissa = FALSE;
 		pstate->forced_exponent_sign = TRUE;
 		break;
+	case 'E':
+		if (GET_TOKEN (pstate->tno_E + 2).token != 'E') {
 #ifdef ALLOW_EE_MARKUP
-	case 'E': {
-		int i;
+			int i;
 
-		use_markup = TRUE;
-		pstate->forced_exponent_sign =
-			(GET_TOKEN (tno_exp_start).token == '+');
-		if (pstate->forced_exponent_sign)
-			tno_exp_start++;
+			use_markup = TRUE;
+			pstate->forced_exponent_sign =
+				(GET_TOKEN (tno_exp_start).token == '+');
+			if (pstate->forced_exponent_sign)
+				tno_exp_start++;
 
-		simplify_mantissa = TRUE;
-		for (i = 0; i < pstate->tno_E; i++) {
-			if (GET_TOKEN(i).token == TOK_DECIMAL)
-				break;
-			if (GET_TOKEN(i).token == '0') {
-				simplify_mantissa = FALSE;
-				break;
+			simplify_mantissa = TRUE;
+			for (i = 0; i < pstate->tno_E; i++) {
+				if (GET_TOKEN(i).token == TOK_DECIMAL)
+					break;
+				if (GET_TOKEN(i).token == '0') {
+					simplify_mantissa = FALSE;
+					break;
+				}
 			}
-		}
 
-		break;
-	}
+			break;
 #endif
+		} else {
+#ifdef ALLOW_SI_FORMAT
+			use_SI = TRUE;
+			tno_exp_start += 2;
+			break;
+#endif
+		}
+		/* Fall through */
 	default:
 		return NULL;
 	}
@@ -1779,8 +1815,13 @@ go_format_parse_number_E (GOFormatParseState *pstate)
 		ADD_OP2 (OP_CHAR, 'u');
 		ADD_OP2 (OP_CHAR, 'p');
 		ADD_OP2 (OP_CHAR, '>');
-	} else
+	}
+#endif
+#ifdef ALLOW_SI_FORMAT
+	if (use_SI)
+		ADD_OP (OP_NUM_SI_EXPONENT);
 #endif
+	if (!use_markup && !use_SI)
 		ADD_OP2 (OP_CHAR, 'E');
 
 	if (!go_format_parse_number_new_1 (prg, pstate,
@@ -1797,7 +1838,7 @@ go_format_parse_number_E (GOFormatParseState *pstate)
 		ADD_OP2 (OP_CHAR, 'p');
 		ADD_OP2 (OP_CHAR, '>');
 		if (simplify_mantissa)
-			ADD_OP (OP_NUM_SIMPLIFY_EXPONENT);
+			ADD_OP (OP_NUM_SIMPLIFY_MARKUP_EXPONENT);
 	}
 #endif
 
@@ -1807,6 +1848,7 @@ go_format_parse_number_E (GOFormatParseState *pstate)
 	fmt->u.number.program = g_string_free (prg, FALSE);
 	fmt->u.number.E_format = TRUE;
 	fmt->u.number.use_markup = use_markup;
+	fmt->u.number.use_SI = use_SI;
 	fmt->u.number.scale_is_2 = (pstate->scale == 2);
 	return fmt;
 }
@@ -2319,7 +2361,10 @@ go_format_dump_program (const guchar *prg)
 #ifdef ALLOW_EE_MARKUP
 		REGULAR(OP_NUM_MARK_MANTISSA);
 		REGULAR(OP_NUM_SIMPLIFY_MANTISSA);
-		REGULAR(OP_NUM_SIMPLIFY_EXPONENT);
+		REGULAR(OP_NUM_SIMPLIFY_MARKUP_EXPONENT);
+#endif
+#ifdef ALLOW_SI_FORMAT
+		REGULAR(OP_NUM_SI_EXPONENT);
 #endif
 		REGULAR(OP_NUM_FRACTION_WHOLE);
 		REGULAR(OP_NUM_FRACTION_NOMINATOR);
@@ -2506,6 +2551,9 @@ SUFFIX(go_format_execute) (PangoLayout *layout, GString *dst,
 	int mantissa_start = -1;
 	int special_mantissa = INT_MAX;
 #endif
+#ifdef ALLOW_SI_FORMAT
+	gboolean inhibit_number = FALSE;
+#endif
 	char *oldlocale = NULL;
 
 	memset (&fraction, 0, sizeof (fraction));
@@ -2931,6 +2979,14 @@ SUFFIX(go_format_execute) (PangoLayout *layout, GString *dst,
 			break;
 
 		case OP_NUM_MOVETO_ONES: {
+#ifdef ALLOW_SI_FORMAT
+			if (inhibit_number) {
+				numi = dotpos = 0;
+				numpos = -1;
+				g_string_truncate (numtxt, 0);
+				break;
+			}
+#endif
 			numi = dotpos;
 			/* Ignore the zero in "0.xxx" */
 			if (numi == 1 && numtxt->str[0] == '0' && numtxt->str[dotpos] != 0)
@@ -2971,6 +3027,10 @@ SUFFIX(go_format_execute) (PangoLayout *layout, GString *dst,
 			char fc = *prg++;
 			char c;
 			if (numi == 0) {
+#ifdef ALLOW_SI_FORMAT
+				if (inhibit_number)
+					break;
+#endif
 				if (fc == '0')
 					c = '0';
 				else if (fc == '?')
@@ -3030,6 +3090,10 @@ SUFFIX(go_format_execute) (PangoLayout *layout, GString *dst,
 
 		case OP_NUM_EXPONENT_SIGN: {
 			gboolean forced = (*prg++ != 0);
+#ifdef ALLOW_SI_FORMAT
+			if (inhibit_number)
+				break;
+#endif
 			if (exponent >= 0) {
 				if (forced)
 					g_string_insert_c (dst, numpos, '+');
@@ -3056,7 +3120,7 @@ SUFFIX(go_format_execute) (PangoLayout *layout, GString *dst,
 				g_string_truncate (dst, mantissa_start);
 			break;
 
-		case OP_NUM_SIMPLIFY_EXPONENT:
+		case OP_NUM_SIMPLIFY_MARKUP_EXPONENT:
 			if (special_mantissa == 0) {
 				g_string_truncate (dst, mantissa_start);
 				g_string_append_c (dst, '0');
@@ -3064,6 +3128,19 @@ SUFFIX(go_format_execute) (PangoLayout *layout, GString *dst,
 			break;
 #endif
 
+#ifdef ALLOW_SI_FORMAT
+		case OP_NUM_SI_EXPONENT:
+			if (exponent >= -24 &&
+			    exponent <= 24 &&
+			    (exponent + 300) % 3 == 0) {
+				g_string_append (dst,
+						 si_prefixes[(24 - exponent) / 3]);
+				inhibit_number = TRUE;
+			} else
+				g_string_append_c (dst, 'E');
+			break;
+#endif
+
 		case OP_NUM_FRACTION: {
 			gboolean wp = *prg++;
 			gboolean explicit_denom = *prg++;
