PHP_METHOD(Money, multiply) { double factor; volatile double dresult; long rounding_mode = PHP_ROUND_HALF_UP, lresult; if (zend_parse_parameters(ZEND_NUM_ARGS(), "d|l", &factor, &rounding_mode) == FAILURE) { return; } if (UNEXPECTED(rounding_mode < 0 || rounding_mode > PHP_ROUND_HALF_ODD)) { zend_throw_exception(spl_ce_InvalidArgumentException, "$roundingMode must be a valid rounding mode (PHP_ROUND_*)", 0); return; } dresult = _php_math_round(factor * Z_LVAL_P(zend_read_property(money_ce, getThis(), MONEY_PROP_AMOUNT_WS, 0)), 0, rounding_mode); lresult = zend_dval_to_lval(dresult); if (UNEXPECTED(lresult & LONG_SIGN_MASK)) { zend_throw_exception(spl_ce_OverflowException, "Integer overflow", 0); return; } CREATE_NEW_MONEY_OBJ(return_value, lresult, zend_read_property(money_ce, getThis(), MONEY_PROP_CURRENCY_WS, 0)); }
PHPAPI zend_string *_php_math_number_format_ex(double d, int dec, char *dec_point, size_t dec_point_len, char *thousand_sep, size_t thousand_sep_len) { zend_string *res; zend_string *tmpbuf; char *s, *t; /* source, target */ char *dp; int integral; int reslen = 0; int count = 0; int is_negative=0; if (d < 0) { is_negative = 1; d = -d; } dec = MAX(0, dec); d = _php_math_round(d, dec, PHP_ROUND_HALF_UP); tmpbuf = strpprintf(0, "%.*F", dec, d); if (tmpbuf == NULL) { return NULL; } else if (!isdigit((int)ZSTR_VAL(tmpbuf)[0])) { return tmpbuf; } /* find decimal point, if expected */ if (dec) { dp = strpbrk(ZSTR_VAL(tmpbuf), ".,"); } else { dp = NULL; } /* calculate the length of the return buffer */ if (dp) { integral = (int)(dp - ZSTR_VAL(tmpbuf)); } else { /* no decimal point was found */ integral = (int)ZSTR_LEN(tmpbuf); } /* allow for thousand separators */ if (thousand_sep) { integral += (int)(thousand_sep_len * ((integral-1) / 3)); } reslen = integral; if (dec) { reslen += dec; if (dec_point) { reslen += (int)dec_point_len; } } /* add a byte for minus sign */ if (is_negative) { reslen++; } res = zend_string_alloc(reslen, 0); s = ZSTR_VAL(tmpbuf) + ZSTR_LEN(tmpbuf) - 1; t = ZSTR_VAL(res) + reslen; *t-- = '\0'; /* copy the decimal places. * Take care, as the sprintf implementation may return less places than * we requested due to internal buffer limitations */ if (dec) { int declen = (int)(dp ? s - dp : 0); int topad = dec > declen ? dec - declen : 0; /* pad with '0's */ while (topad--) { *t-- = '0'; } if (dp) { s -= declen + 1; /* +1 to skip the point */ t -= declen; /* now copy the chars after the point */ memcpy(t + 1, dp + 1, declen); } /* add decimal point */ if (dec_point) { t -= dec_point_len; memcpy(t + 1, dec_point, dec_point_len); } } /* copy the numbers before the decimal point, adding thousand * separator every three digits */ while (s >= ZSTR_VAL(tmpbuf)) { *t-- = *s--; if (thousand_sep && (++count%3)==0 && s >= ZSTR_VAL(tmpbuf)) { t -= thousand_sep_len; memcpy(t + 1, thousand_sep, thousand_sep_len); } } /* and a minus sign, if needed */ if (is_negative) { *t-- = '-'; } ZSTR_LEN(res) = reslen; zend_string_release(tmpbuf); return res; }
/* {{{ _php_math_number_format */ PHPAPI char *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep) { char *tmpbuf = NULL, *resbuf; char *s, *t; /* source, target */ char *dp; int integral; int tmplen, reslen=0; int count=0; int is_negative=0; if (d < 0) { is_negative = 1; d = -d; } dec = MAX(0, dec); d = _php_math_round(d, dec, PHP_ROUND_HALF_UP); tmplen = spprintf(&tmpbuf, 0, "%.*F", dec, d); if (tmpbuf == NULL || !isdigit((int)tmpbuf[0])) { return tmpbuf; } /* find decimal point, if expected */ if (dec) { dp = strpbrk(tmpbuf, ".,"); } else { dp = NULL; } /* calculate the length of the return buffer */ if (dp) { integral = dp - tmpbuf; } else { /* no decimal point was found */ integral = tmplen; } /* allow for thousand separators */ if (thousand_sep) { integral += (integral-1) / 3; } reslen = integral; if (dec) { reslen += dec; if (dec_point) { reslen++; } } /* add a byte for minus sign */ if (is_negative) { reslen++; } resbuf = (char *) emalloc(reslen+1); /* +1 for NUL terminator */ s = tmpbuf+tmplen-1; t = resbuf+reslen; *t-- = '\0'; /* copy the decimal places. * Take care, as the sprintf implementation may return less places than * we requested due to internal buffer limitations */ if (dec) { int declen = dp ? s - dp : 0; int topad = dec > declen ? dec - declen : 0; /* pad with '0's */ while (topad--) { *t-- = '0'; } if (dp) { s -= declen + 1; /* +1 to skip the point */ t -= declen; /* now copy the chars after the point */ memcpy(t + 1, dp + 1, declen); } /* add decimal point */ if (dec_point) { *t-- = dec_point; } } /* copy the numbers before the decimal point, adding thousand * separator every three digits */ while(s >= tmpbuf) { *t-- = *s--; if (thousand_sep && (++count%3)==0 && s>=tmpbuf) { *t-- = thousand_sep; } } /* and a minus sign, if needed */ if (is_negative) { *t-- = '-'; } efree(tmpbuf); return resbuf; }