php源码学习d3 加法

1.整型相加溢出问题

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

输出结果:结果成double了

 

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 变量存储

猜你喜欢

转载自blog.csdn.net/smile12393/article/details/88531763