天天看點

php源碼學習d3 加法

1.整型相加溢出問題

$a = 18446744073709551616; // 2的64次方
var_dump($a+1);
           

輸出結果:結果成double了

php源碼學習d3 加法

2.PHP源碼中對+的處理

ZEND_API int ZEND_FASTCALL add_function(zval *result, zval *op1, zval *op2) /* {{{ */
{
	zval op1_copy, op2_copy;
	int converted = 0;

	while (1) { // TYPE_PAIR 會先判斷比較兩個參數的類型
		switch (TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2))) {
			case TYPE_PAIR(IS_LONG, IS_LONG):
				fast_long_add_function(result, op1, op2);
				return SUCCESS;
			case TYPE_PAIR(IS_LONG, IS_DOUBLE):
				ZVAL_DOUBLE(result, ((double)Z_LVAL_P(op1)) + Z_DVAL_P(op2));
				return SUCCESS;

			case TYPE_PAIR(IS_DOUBLE, IS_LONG):
				ZVAL_DOUBLE(result, Z_DVAL_P(op1) + ((double)Z_LVAL_P(op2)));
				return SUCCESS;

			case TYPE_PAIR(IS_DOUBLE, IS_DOUBLE):
				ZVAL_DOUBLE(result, Z_DVAL_P(op1) + Z_DVAL_P(op2));
				return SUCCESS;

			case TYPE_PAIR(IS_ARRAY, IS_ARRAY):
				if ((result == op1) && (result == op2)) {
					/* $a += $a */
					return SUCCESS;
				}
				if (result != op1) {
					ZVAL_DUP(result, op1);
				}
				zend_hash_merge(Z_ARRVAL_P(result), Z_ARRVAL_P(op2), zval_add_ref, 0);
				return SUCCESS;

			default:
				if (Z_ISREF_P(op1)) {
					op1 = Z_REFVAL_P(op1);
				} else if (Z_ISREF_P(op2)) {
					op2 = Z_REFVAL_P(op2);
				} else if (!converted) {
					ZEND_TRY_BINARY_OBJECT_OPERATION(ZEND_ADD, add_function);

					zendi_convert_scalar_to_number(op1, op1_copy, result, 0);
					zendi_convert_scalar_to_number(op2, op2_copy, result, 0);
					converted = 1;
				} else {
					zend_throw_error(NULL, "Unsupported operand types");
					return FAILURE; /* unknown datatype */
				}
		}
	}
}
           
// 兩個int型相加進入此,彙編溢出更換類型(ps彙編那段沒看懂)
static zend_always_inline void fast_long_add_function(zval *result, zval *op1, zval *op2)
{
#if defined(__GNUC__) && defined(__i386__) \
	&& !(4 == __GNUC__ && 8 == __GNUC_MINOR__) \
	&& !(4 == __GNUC__ && 9 == __GNUC_MINOR__ && (defined(__PIC__) || defined(__PIE__)))
	/* Position-independent builds fail with gcc-4.9.x */
	__asm__(
		"movl	(%1), %%eax\n\t"
		"addl   (%2), %%eax\n\t"
		"jo     0f\n\t"
		"movl   %%eax, (%0)\n\t"
		"movl   %3, %c5(%0)\n\t"
		"jmp    1f\n"
		"0:\n\t"
		"fildl	(%1)\n\t"
		"fildl	(%2)\n\t"
		"faddp	%%st, %%st(1)\n\t"
		"movl   %4, %c5(%0)\n\t"
		"fstpl	(%0)\n"
		"1:"
		:
		: "r"(&result->value),
		  "r"(&op1->value),
		  "r"(&op2->value),
		  "n"(IS_LONG),
		  "n"(IS_DOUBLE),
		  "n"(ZVAL_OFFSETOF_TYPE)
		: "eax","cc", "memory");
#elif defined(__GNUC__) && defined(__x86_64__)
	__asm__(
		"movq	(%1), %%rax\n\t"
		"addq   (%2), %%rax\n\t"
		"jo     0f\n\t"
		"movq   %%rax, (%0)\n\t"
		"movl   %3, %c5(%0)\n\t"
		"jmp    1f\n"
		"0:\n\t"
		"fildq	(%1)\n\t"
		"fildq	(%2)\n\t"
		"faddp	%%st, %%st(1)\n\t"
		"movl   %4, %c5(%0)\n\t"
		"fstpl	(%0)\n"
		"1:"
		:
		: "r"(&result->value),
		  "r"(&op1->value),
		  "r"(&op2->value),
		  "n"(IS_LONG),
		  "n"(IS_DOUBLE),
		  "n"(ZVAL_OFFSETOF_TYPE)
		: "rax","cc", "memory");
#else
	/*
	 * 'result' may alias with op1 or op2, so we need to
	 * ensure that 'result' is not updated until after we
	 * have read the values of op1 and op2.
	 */

	if (UNEXPECTED((Z_LVAL_P(op1) & LONG_SIGN_MASK) == (Z_LVAL_P(op2) & LONG_SIGN_MASK)
		&& (Z_LVAL_P(op1) & LONG_SIGN_MASK) != ((Z_LVAL_P(op1) + Z_LVAL_P(op2)) & LONG_SIGN_MASK))) {
		ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2));
	} else {
		ZVAL_LONG(result, Z_LVAL_P(op1) + Z_LVAL_P(op2));
	}
#endif
}
           

3.兩個數求平均值

(x&y)+ (x^y)>> 1

 (x|y) - (x^y)>>1

簡單了解:

 1.(A+B)/2用二進制辨別

 ((a1+b1)*2^1 + (a2+b2)*2^2 + (a3+b3)*2^3+ (a4+b4)*2^n)/2

 2.即計算系數平均值相加即可,可簡化為計算(a1+b1)/2

 3.對于二進制,隻有以下四種情況

a1    b1

1       1 // 相加除2為1,與運算和或運算均可滿足,但是2,3中情況或運算也會為1,是以需要減去

1       0 // 相加除2為0.5,先異或右移可以滿足

0       1 // 相加除2為0.5,先異或右移可以滿足

0        0 // 相加除2還是0,無需處理

4.筆記位址

d4 變量存儲

繼續閱讀