php 的加法

无意间看到了php中关于加,减,乘,除 的计算方法

这里

http://lxr.php.net/source/xref/PHP-5.6/Zend/zend_operators.h#596

static zend_always_inline int fast_add_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
597{
598    if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
599        if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
600#if defined(__GNUC__) && defined(__i386__)
601        __asm__(
602            "movl    (%1), %%eax
	"
603            "addl   (%2), %%eax
	"
604            "jo     0f
	"
605            "movl   %%eax, (%0)
	"
606            "movb   %3, %c5(%0)
	"
607            "jmp    1f
"
608            "0:
	"
609            "fildl    (%1)
	"
610            "fildl    (%2)
	"
611            "faddp    %%st, %%st(1)
	"
612            "movb   %4, %c5(%0)
	"
613            "fstpl    (%0)
"
614            "1:"
615            :
616            : "r"(&result->value),
617              "r"(&op1->value),
618              "r"(&op2->value),
619              "n"(IS_LONG),
620              "n"(IS_DOUBLE),
621              "n"(ZVAL_OFFSETOF_TYPE)
622            : "eax","cc");
623#elif defined(__GNUC__) && defined(__x86_64__)
624        __asm__(
625            "movq    (%1), %%rax
	"
626            "addq   (%2), %%rax
	"
627            "jo     0f
	"
628            "movq   %%rax, (%0)
	"
629            "movb   %3, %c5(%0)
	"
630            "jmp    1f
"
631            "0:
	"
632            "fildq    (%1)
	"
633            "fildq    (%2)
	"
634            "faddp    %%st, %%st(1)
	"
635            "movb   %4, %c5(%0)
	"
636            "fstpl    (%0)
"
637            "1:"
638            :
639            : "r"(&result->value),
640              "r"(&op1->value),
641              "r"(&op2->value),
642              "n"(IS_LONG),
643              "n"(IS_DOUBLE),
644              "n"(ZVAL_OFFSETOF_TYPE)
645            : "rax","cc");
646#else
647            /*
648             * 'result' may alias with op1 or op2, so we need to
649             * ensure that 'result' is not updated until after we
650             * have read the values of op1 and op2.
651             */
652
653            if (UNEXPECTED((Z_LVAL_P(op1) & LONG_SIGN_MASK) == (Z_LVAL_P(op2) & LONG_SIGN_MASK)
654                && (Z_LVAL_P(op1) & LONG_SIGN_MASK) != ((Z_LVAL_P(op1) + Z_LVAL_P(op2)) & LONG_SIGN_MASK))) {
655                Z_DVAL_P(result) = (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2);
656                Z_TYPE_P(result) = IS_DOUBLE;
657            } else {
658                Z_LVAL_P(result) = Z_LVAL_P(op1) + Z_LVAL_P(op2);
659                Z_TYPE_P(result) = IS_LONG;
660            }
661#endif
662            return SUCCESS;
663        } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) {
664            Z_DVAL_P(result) = ((double)Z_LVAL_P(op1)) + Z_DVAL_P(op2);
665            Z_TYPE_P(result) = IS_DOUBLE;
666            return SUCCESS;
667        }
668    } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) {
669        if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) {
670            Z_DVAL_P(result) = Z_DVAL_P(op1) + Z_DVAL_P(op2);
671            Z_TYPE_P(result) = IS_DOUBLE;
672            return SUCCESS;
673        } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
674            Z_DVAL_P(result) = Z_DVAL_P(op1) + ((double)Z_LVAL_P(op2));
675            Z_TYPE_P(result) = IS_DOUBLE;
676            return SUCCESS;
677        }
678    }
679    return add_function(result, op1, op2 TSRMLS_CC);
680}

其中第653行中的宏 LONG_SIGN_MASK 定义为

#define LONG_SIGN_MASK (1L << (8*sizeof(long)-1))

 在64位机下,LONG_SIGN_MASK的值为 1L<< (8*8-1) = 1L<<63 = 2^63次方

Intel IA32 浮点运算

IA32处理器和很多其它一些处理器一样,有专门用于保存浮点数的寄存器,当在cpu中进行浮点数运算时,这些寄存器就用来保存输入输出及相关的中间结果。

但IA32有一个比较特别的地方,它的浮点数寄存器是80位的,而我们在程序中只用到32和64位两种类型,因此当把float,double放入到cpu中时,它们都会先被转换成了80位,然后以80位的方式进行运算,最后得到的结果再转换回来。这样特性使得浮点数的计算可以相对更精确些,但同时,一不小心很可能也会引出一些意想不到的问题。

你可能突然恍然大悟了,对的,我们最开始提到那个奇怪的问题就与此相关。

s/e得到结果是个80位的浮点数,由这个浮点数先转换成double再转成int,与直接就转换成int,结果很可能是不同的。

比如在我们的例子中,s/e ~ 29.999999....时,s/e转换成double使用round-to-even的方式,会得到也许是30.0000001,再转成整形时,得到30.

但如果直接由29.99999...转换成整型,得到却是29。

后来新出的系列Intel处理器,包括IA32及64位的处理器,提供了专门的硬件来直接处理浮点数,使得可以分开对待float型与double型,这些硬件特性在compiler的支持下,可以生相对高效的代码,同时也避免了我们上面所遇到的问题,有兴趣的读者可以google一下相关的关键字:sse。

 在64位机下
int           4个字节

long         8个字节

double        8个字节

float         4个字节

double long  8个字节

指针         8个字节

而在32位机下

int            4个字节

long         4个字节

double        8个字节

float         4个字节

double long  8个字节

指针                 4个字节

原文地址:https://www.cnblogs.com/taek/p/6067652.html