在如今的主流 Web 编程语言中,如 PHP 或 Python 等,都包含幂运算符(一般来说符号是 ^ 或者 **)。而最新的 ES7 中也增加了对幂运算的支持,使用符号 **,最新的 Chrome 已经提供了对幂运算的支持。

但是在 javascript 中,** 运算有时候并不等于 Math.pow(a,b),在最新的 Chrome 55 中:

Math.pow(99,99) 的结果是 3.697296376497263e+197,

但是 99**99 的结果是 3.697296376497268e+197。

两者并不相等

3.697296376497263e+197

3.697296376497268e+197

而且 Math.pow(99,99) - 99**99 的结果也不是 0 而是 -5.311379928167671e+182。

因此我们猜测,** 操作符只是幂运算的另一个实现。但是当我们写一个函数时,幂运算又表现出诡异的特性:

function diff(x) {

return Math.pow(x,x) - x**x;

}

调用 diff(99) 返回 0。WTF?两者又相等了!

猜猜下面代码输出什么?

var x = 99;

x**x - 99**99;

这段代码的运行结果是 -5.311379928167671e+182。

这简直是薛定谔的幂。

究其原因,V8 引擎使用了常量折叠(const folding)。常量折叠是一种编译器的编译优化技术。

考虑如下代码:

for (let i = 0; i < 100*100*100; i++){

// 循环体

}

该循环的条件 i<100*100*100 是一个表达式(expression),如果放到判断时再求值那么 100*100*100 的计算将会进行 1000000 次。如果编译器在语法分析阶段进行常量合并,该循环将会变为这样:

for (let i = 0; i < 1000000; i++){

// 循环体

}

而上文中提到的 99**99 的计算也使用到了常量折叠。也就是说 99**99 是在编译时进行计算(常量折叠),而 Math.pow 总是在运行时进行计算。当我们使用变量进行幂运算时(例 a**b)此时不存在常量折叠,因此 a ** b 的值在运行时进行计算,** 会被编译成 Math.pow 调用。

在源码 src/parsing/parser.cc 文件中,编译时计算代码:

case Token::EXP: {

double value = Pow(x_val, y_val);

int int_value = static_cast(value);

*x = factory()->NewNumberLiteral(

int_value == value && value != -0.0 ? int_value : value, pos,

has_dot);

return true;

可以看到使用了 Pow 函数计算了幂运算的求值结果。Pow 是一个 inline 的函数,内部做了一些常规优化,对不能优化的情况则使用了 std::pow(x, y) 来计算最终结果。

而 Math.pow 的算法为:

// ES6 section 20.2.2.26 Math.pow ( x, y )

TF_BUILTIN(MathPow, CodeStubAssembler) {

Node* x = Parameter(1);

Node* y = Parameter(2);

Node* context = Parameter(5);

Node* x_value = TruncateTaggedToFloat64(context, x);

Node* y_value = TruncateTaggedToFloat64(context, y);

Node* value = Float64Pow(x_value, y_value);

Node* result = ChangeFloat64ToTagged(value);

Return(result);

}

可见两者使用了不同的算法。但是当不做常量折叠的时候,** 则转换成了 Math.pow 函数调用:

Expression* Parser::RewriteExponentiation(

Expression* left,

Expression* right,

int pos) {

ZoneList* args = new (zone()) ZoneList(2, zone());

args->Add(left, zone());

args->Add(right, zone());

return factory()->NewCallRuntime(Context::MATH_POW_INDEX, args, pos);

}

于是就造成了 ** 有时不等于 Math.pow 的怪异问题。再看看如下代码:

console.log(99**99);

a = 99, b = 99;

console.log(a**b);

console.log(Math.pow(99, 99));

分别输出:

3.697296376497268e+197

3.697296376497263e+197

3.697296376497263e+197

其实

9999=369729637649726772657187905628805440595668764281741102430259972423552570455277523421410650010128232727940978889548326540119429996769494359451621570193644014418071060667659301384999779999159200499899

因此第一个结果更接近准确的值。

上周(2017年1月16日)这个怪异的行为已经作为一个 bug 提交给了 V8 项目,bug 编号 #5848。

java 常量折叠_V8 使用“常量折叠”优化技巧,导致幂(**)运算有时候不等于 Math.pow()...相关推荐

  1. 【代码调优】Java开发中总结的代码质量优化技巧,springboot企业级开发教程

    SELECT * FROM db_user WHERE username='validuser' OR '1'='1' AND password='' 同样,攻击者可以为password提供如下字符串 ...

  2. Java中int型如何求幂_关于java:为什么Math.pow(int,int)慢于我的幼稚实现?

    昨天我看到一个问题,问为什么Math.pow(int,int)这么慢,但是问题措辞不佳,没有进行任何研究,因此很快就关闭了. 我做了一些自我测试,发现与整数参数相比,Math.pow方法实际上比我自己 ...

  3. java pow实现_实现Java Math.pow(double a,double b) 方法

    使用 Java 开发移动设备应用程序时,可能需要用到特定 Java VM 所没有的数学方法.本文将专门解决 Java ME 没有"幂"方法 Math.pow() 的问题.我们将演示 ...

  4. Java的开方和次方Math.sqrt()方法和Math.pow()方法以及求解有多少种连续正整数之和为N的算法详解

    1. Java的开方和次方运算 java.lang.Math.sqrt(double a) 返回一个数字a的正平方根,返回结果是double型 java.lang.Math.pow(double a, ...

  5. [转载] java常量池-字符串常量池、class常量池和运行时常量池

    参考链接: 如何在Java中初始化和比较字符串 原文链接:http://tangxman.github.io/2015/07/27/the-difference-of-java-string-pool ...

  6. java 常量定义_Java中常量定义的几种方式

    编程中使用常量的优点: 常量提取出来有利于代码阅读,而且下次再做这种判断不用手写或复制并且提高代码的复用率,方便修改,直接通过常量类就能得到.不过我觉得提取出来并不会有利于代码性能提升,因为常量分配在 ...

  7. Java 静态变量,静态方法,静态常量(java static 关键字)

    Java 静态变量,静态方法,静态常量  就是变量 ,方法,常量前面添加了static 关键字 为什么要使用静态的呢 有时候在处理问题时会需要两个类在同一个内存区域共享一个数据, 不如现在 Main ...

  8. Java虚拟机的静态常量池和运行时常量池

    (静态)常量池:用于存放编译器生成的各种字面量和符号引用(符号引用区别于直接引用,后者在class字节码文件被虚拟机解析之后,符号引用将被替换为直接引用). 运行时常量池:(静态)常量池中的内容在类加 ...

  9. 好好说说Java中的常量池之Class常量池

    前言 在Java中,常量池的概念想必很多人都听说过.这也是面试中比较常考的题目之一.在Java有关的面试题中,一般习惯通过String的有关问题来考察面试者对于常量池的知识的理解,几道简单的Strin ...

最新文章

  1. OpenResty中遇到Can't locate Time/HiRes.pm in @INC问题的解决方法
  2. linux screen 常用命令
  3. SD-WAN可以提高WAN性能的3种方式—Vecloud
  4. virtual box挂载 共享文件夹
  5. what kind of supervision will i do next semester?
  6. Boost:排序的bimap双图的测试程序
  7. 填充因子-FILL FACTOR
  8. [HZNOI #koishi] Magic
  9. P4316 绿豆蛙的归宿 期望dp + DAG
  10. junit rule_使用@Rule在JUnit中测试文件和目录
  11. ios7 导航栏 手势 右划 自动返回 相关
  12. WIN10+VS2015环境下安装PCL1.8.1
  13. RayMarching3:组合与变幻
  14. python序列中各元素之间存在顺序关系_《Python Cookbook(第3版)中文版》——1.10 从序列中移除重复项且保持元素间顺序不变-阿里云开发者社区...
  15. 史陶比尔机器人CS9控制器及SP2示教器使用简易指南
  16. CEIWEI USBMonitor USB监控精灵 v2.3.2 USB过滤驱动
  17. 哈工大物理学专业计算机知识怎样,哈尔滨工业大学应用物理学专业怎么样
  18. 转载一篇过来人谈的大学期间关于课外学习的意见lt;出自左飞老师笔记gt;
  19. hsv白色h值是多少_rgb颜色模型与hsv颜色模型的理解
  20. 关于检索的知识[转]

热门文章

  1. iqueryable怎么进行操作_C# 数据操作系列 - 16 SqlSugar 完结篇(最后的精华)
  2. 【控制】《多智能体系统一致性协同演化控制理论与技术》纪良浩老师-第15章-基于竞争关系的离散异构多智能体系统分组一致性
  3. 第十二章 支持向量机-机器学习老师板书-斯坦福吴恩达教授
  4. STM32 进阶教程 8 - 位带操作
  5. python 2021/12/31
  6. 数字IC设计bilibili-Designer Compiler的理论笔记+实操
  7. linux下的access()函数判断文件是否存在、打印时间
  8. 【问题解决】老外ESP32TTS语音合成库AquesTalk ESP32测试成功
  9. 基于SIMULINK的六足机器人仿真
  10. Linux下GBK文件编码批量转换UTF-8命令