Fork me on GitHub

分类 技术相关 下的文章

【转】PHP浮点数问题

作者: Laruence( )
本文地址: http://www.laruence.com/2013/03/26/2884.html
转载请注明出处

关于PHP的浮点数, 我之前写过一篇文章: 关于PHP浮点数你应该知道的(All ‘bogus’ about the float in PHP)

不过, 我当时遗漏了一点, 也就是对于如下的这个常见问题的回答:

    <?php
        $f = 0.58;
        var_dump(intval($f * 100)); //为啥输出57
    ?>

为啥输出是57啊? PHP的bug么?

我相信有很多的同学有过这样的疑问, 因为光问我类似问题的人就很多, 更不用说bugs.php.net上经常有人问…

要搞明白这个原因, 首先我们要知道浮点数的表示(IEEE 754):

浮点数, 以64位的长度(双精度)为例, 会采用1位符号位(E), 11指数位(Q), 52位尾数(M)表示(一共64位).

符号位:最高位表示数据的正负,0表示正数,1表示负数。

指数位:表示数据以2为底的幂,指数采用偏移码表示

尾数:表示数据小数点后的有效数字.

这里的关键点就在于, 小数在二进制的表示, 关于小数如何用二进制表示, 大家可以百度一下, 我这里就不再赘述, 我们关键的要了解, 0.58 对于二进制表示来说, 是无限长的值(下面的数字省掉了隐含的1)..

0.58的二进制表示基本上(52位)是: 0010100011110101110000101000111101011100001010001111
0.57的二进制表示基本上(52位)是: 0010001111010111000010100011110101110000101000111101

而两者的二进制, 如果只是通过这52位计算的话,分别是:

0.58 -> 0.57999999999999996
0.57 -> 0.56999999999999995

至于0.58 100的具体浮点数乘法, 我们不考虑那么细, 有兴趣的可以看(Floating point), 我们就模糊的以心算来看… 0.58 100 = 57.999999999

那你intval一下, 自然就是57了….

可见, 这个问题的关键点就是: “你看似有穷的小数, 在计算机的二进制表示里却是无穷的”

so, 不要再以为这是PHP的bug了, 这就是这样的…..

PHP实现四则运算表达式求值(逆波兰表示法)

PHP实现四则运算表达式求值(逆波兰表示法)

中缀表达式:1+(3+(5*6)/4-2*8)
后缀表达式(逆波兰):1 3 5 6 * 4 / + 2 8 * - +

class Calculator
{
    public function execute($express)
    {
        //中缀表达式转后缀表达式
        $back = $this->midToBack($express);
        //计算后缀表达式
        return $this->calculateBack($back);
    }

    /**
     * 计算后缀表达式
     * 规则:
     * 从左到右遍历表达式的每个数字和符号,遇到数字就进栈,遇到符号就将栈顶两个数字出栈,进行运算,运算结果进栈,一直到最终获得结果
     *
     * @param $backExpress
     */
    private function calculateBack(array $backExpress)
    {
        $stack = [];
        foreach ($backExpress as $be){
            if(is_numeric($be)){
                $stack[] = $be;
            }else{
                $o2 = array_pop($stack);
                $o1 = array_pop($stack);
                switch ($be){
                    case '+':
                        $stack[] = $o1 + $o2;
                        break;
                    case '-':
                        $stack[] = $o1 - $o2;
                        break;
                    case '*':
                        $stack[] = $o1 * $o2;
                        break;
                    case '/':
                        $stack[] = (float)$o1 / (float)$o2;
                        break;
                    default:
                }
            }
        }
        return array_pop($stack);
    }

    /**
     * 规则:
     * 从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分。
     * 若是符号,则判断其与栈顶符号的优先级,是右括号则依次出栈直到左括号为止;或是栈顶符号高于等于当前符号优先级,则栈顶符号依次出栈直到
     * 栈顶符号优先级小于当前符号为止,然后将当前符号入栈;遍历结束后检查栈内是否还有元素,如果有则依次出栈成为后缀表达式的一部分。
     **/
    private function midToBack($midExpress)
    {
        $back = [];
        $symbol = [];
        preg_match_all('/\d+|[\+\-\*\/\(\)]/', $midExpress, $matches);
        if(empty($matches)){
            throw new \Exception('invalid express');
        }
        $expresses = $matches[0];
        foreach ($expresses as $exp) {
            if(is_numeric($exp)){
                $back[] = $exp;
            }else{
                if($exp==')'){
                    $count = count($symbol);
                    for($i=0;$i<$count;$i++){
                        $s = array_pop($symbol);
                        if($s=='('){
                            break;
                        }
                        $back[] = $s;
                    }
                }else{
                    $count = count($symbol);
                    for($i=0;$i<$count;$i++){
                        $top = $symbol[count($symbol)-1];
                        if($this->compareSymbol($exp,$top)<=0){
                            $s = array_pop($symbol);
                            $back[] = $s;
                        }else{
                            break;
                        }
                    }
                    $symbol[] = $exp;
                }

            }
        }
        if(!empty($symbol)){
            $count = count($symbol);
            for($i=0;$i<$count;$i++){
                $s = array_pop($symbol);
                $back[] = $s;
            }
        }
        return $back;
    }

    /**
     * 比较符号优先级
     **/
    private function compareSymbol($s1, $s2)
    {
        $symbol = ['+'=>1,'-'=>1,'*'=>2,'/'=>2];
        //如果输入为括号
        if(!isset($symbol[$s1])|| !isset($symbol[$s2])){
            return 1;
        }
        $v1 = $symbol[$s1];
        $v2 = $symbol[$s2];
        $res = 0;
        if($v1 > $v2){
            $res = 1;
        }elseif($v1<$v2){
            $res = -1;
        }
        return $res;
    }

使用Let's Encrypt免费SSL证书实现全站HTTPS

使用Let's Encrypt免费SSL证书实现全站HTTPS

Let’s Encrypt is a free, automated, and open Certificate Authority.

由 ISRG(Internet Security Research Group,互联网安全研究小组)提供服务,并得到了 Mozilla、Cisco、Akamai、Electronic Frontier Foundation 和 Chrome 等众多公司和机构的支持,发展十分迅猛。它还有一个优点是免费,缺点是证书有效期90天和不支持泛域名配置,但是它可以自动续期,一个证书里可以添加多个域名。

- 阅读剩余部分 -

搭建基于es6的react开发环境

安装Node.js与npm

Node.js 是javascript的运行时环境,npm 是javascript的包管理工具
安装成功后,在项目根目录运行npm init初始化当前项目

安装react,es6,babel

npm install -D react react-dom babel-preset-react babel-preset-es2015 babel-loader babel-core

.babelrc文件中配置babel:

{
  "presets": [
    "es2015",
    "react"
  ]
}

- 阅读剩余部分 -

语义化版本号及其约束

语义化版本号及其约束

规范

版本格式:主版本号.次版本号.修订号,版本号递增规则如下:

  1. 主版本号:当APP做了大幅的修改,
  2. 次版本号:当APP功能性新增,
  3. 修订号:问题修正。

先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面,作为延伸。 --- 参考《语义化版本号》

约束

版本约束可以用几个不同的方法来指定。

名称实例描述
确切的版本号1.0.2你可以指定包的确切版本。
范围>=1.0 >=1.0,<2.0 >=1.0,<1.1丨>=1.2通过使用比较操作符可以指定有效的版本范围。有效的运算符:>>=<<=!=。 你可以定义多个范围,用逗号隔开,这将被视为一个逻辑AND处理。一个管道符号将作为逻辑OR处理。
AND 的优先级高于 OR。
通配符1.0.*你可以使用通配符*来指定一种模式。1.0.*>=1.0,<1.1是等效的。
波浪号运算符~1.2标记你所依赖的最低版本,像 ~1.2 (允许1.2以上的任何版本,但不包括2.0)。~1.2相当于>=1.2,<2.0

例如:

表达式 1.8.0|>2.0,<3.0|3.1.*|~4.2
等同于 v==1.8.0 || (v>2.0 && v<3.0) || (v>=3.1.0 && v<3.2.0) || (v>=4.2 && v<5.0)

PHP 嵌入式 SAPI

  php构建了一个层级系统. 最高层是提供用户空间函数和类库的所有扩 展. 同时, 其下是服务API(SAPI)层, 它扮演了webserver(比如apache, iis以及命令行接口 cli)的接口.在这许多sapi实现中有一个特殊的sapi就是嵌入式sapi.
  当这个sapi实现被构建时, 将 会创建一个包含所有你已知的php和zend api函数以及变量的库对象, 这个库对象还包含一些额外的帮助函数和宏, 用以简化外部程序的调用.


- 阅读剩余部分 -

Mac OS X shell 颜色配置

对于从Unix/Linux平台转到Mac的同学来说,“终端”是经常要使用的一个工具。不过可能有很多人已经发现了,当我们使用ls命令来显示目 录内容的时候,“终端”对于目录、可执行文件等特殊类型的文件并没有使用颜色来显示,只有使用“ls -G”时,才能显示颜色,这可真是不方便啊。有没有方法可以默认显示颜色呢?方法当然有。

- 阅读剩余部分 -

mac/linux终端光标的快捷键操作

常用的快捷键:
Ctrl + d        删除一个字符,相当于通常的Delete键(命令行若无所有字符,则相当于exit;处理多行标准输入时也表示eof)
Ctrl + h        退格删除一个字符,相当于通常的Backspace键
Ctrl + u        删除光标之前到行首的字符
Ctrl + k        删除光标之前到行尾的字符
Ctrl + c        取消当前行输入的命令,相当于Ctrl + Break
Ctrl + a        光标移动到行首(Ahead of line),相当于通常的Home键
Ctrl + e        光标移动到行尾(End of line)
Ctrl + f        光标向前(Forward)移动一个字符位置
Ctrl + b        光标往回(Backward)移动一个字符位置
Ctrl + l        清屏,相当于执行clear命令
Ctrl + p        调出命令历史中的前一条(Previous)命令,相当于通常的上箭头
Ctrl + n        调出命令历史中的下一条(Next)命令,相当于通常的上箭头
Ctrl + r        显示:号提示,根据用户输入查找相关历史命令(reverse-i-search)
  
次常用快捷键:
Alt + f         光标向前(Forward)移动到下一个单词
Alt + b         光标往回(Backward)移动到前一个单词
Ctrl + w        删除从光标位置前到当前所处单词(Word)的开头
Alt + d         删除从光标位置到当前所处单词的末尾
Ctrl + y        粘贴最后一次被删除的单词

rsa签名以及java验签实现

在开放平台领域,需要给isv提供sdk,签名是Sdk中需要提供的功能之一。由于isv使用的开发语言不是单一的,因此sdk需要提供多种语言的版本。譬如java、php、c#。另外,在电子商务尤其是支付领域,对安全性的要求比较高,所以会采用非对称密钥RSA

本文主要介绍如何基于java、php、c#在客户端使用rsa签名,然后在服务端使用Java验签。

- 阅读剩余部分 -