lee*_*ggs 37 javascript numbers
在JavaScript中,数字被定义为64位双精度.对于分布式Web应用程序,我有一个特定的用途,只有在我可以依赖所有浏览器的一致结果时才能使用.
尽管使用IEEE标准的规范,我自然怀疑数学库甚至底层硬件的实现可能会有微小的差异,这可能会导致复合错误.
是否有任何兼容性数据来源或可靠的测试套件来验证浏览器中的双精度计算?特别是,我还需要考虑移动浏览器(通常基于ARM).
澄清 -
这是一个关于浏览器兼容性的问题.我试图了解是否可以依赖所有浏览器以IEEE浮点定义的可靠,一致和可重复的方式处理数字.在大多数语言中,这是一个安全的假设,但有趣的是,在浏览器中存在一点不确定性.
关于如何避免由于缺乏精度和舍入误差导致的浮点问题,有一些很好的建议.在大多数情况下,如果您需要准确性,您应该遵循这个建议!
对于这个问题,我不是试图避免这个问题,而是要理解它.浮点数在设计上本质上是不准确的,但只要注意如何进行构建,则不准确性可以完全可预测和一致.IEEE-754将此描述为只有标准组织才能达到的详细程度.
如果有人能引用,我决定提供小额赏金,
在这个问题中,我不是在寻找替代选项,解决方法或避免问题的方法.感谢您的建议.
cko*_*ozl 19
这只是为了好玩,正如你已经说过的那样,我创造了一个新答案,因为这个答案是不同的.但我仍然觉得有一些随意的路人忽略了问题的无效性.那么让我们从解决你的观点开始吧:
首先:
与主流浏览器中IEEE编号实现相关的真正兼容性数据.
doesn't exist, and for that matter doesn't even make any sense, IEEE is just a standards body...? I am not sure if this vague on purpose or on accident, I will assume you were trying to say IEEE 754, but there in lies the rub... there are technically 2 versions of this standard IEEE 754-2008 AND IEEE 754-1985. Basically the former is newer and addresses the latter's oversights. Any sane person would assume that any maintained JavaScript implementation would update to the latest and greatest standard, but any sane person should know JavaScript better than that, and even if JavaScript wasn't crazy, there is no specification saying that the implementation has to be/stay up to date (check the ECMA spec yourself if you don't believe me, they don't even talk "versions"). To compound the matters further the IEEE Standard 754-2008 for Floating-Point Arithmetic supports two encoding formats: the decimal encoding format, and the binary encoding format. Which as would be expected are compatible with each other in the sense that you can go back and forth without loss of data, but that's assuming we have access to the binary representation of the number, which we don't (without attaching a debugger and looking at the store the old school way)
However, from what I can tell it seems it is general practice to "back" a JavaScript Number with an old fashioned double which of course means that we are at the mercy of the compiler used to actually build the browser. But even in that realm, we can't and shouldn't be assuming equality even if all the compilers were on the same version of the standard (they aren't) and even if all the compilers implemented the standard in its entirety (they don't). Here's an excerpt from this paper, which I have deemed an interesting, worthwhile and relevant-to-this-dialog read...
Many programmers like to believe that they can understand the behavior of a program and prove that it will work correctly without reference to the compiler that compiles it or the computer that runs it. In many ways, supporting this belief is a worthwhile goal for the designers of computer systems and programming languages. Unfortunately, when it comes to floating-point arithmetic, the goal is virtually impossible to achieve. The authors of the IEEE standards knew that, and they didn't attempt to achieve it. As a result, despite nearly universal conformance to (most of) the IEEE 754 standard throughout the computer industry, programmers of portable software must continue to cope with unpredictable floating-point arithmetic.
虽然发现我也发现这个参考实现完全在JavaScript中完成(注意:我实际上没有验证实现的有效性).
所有这一切,让我们继续你的第二个请求:
用于验证浏览器内实现的测试套件,包括验证64位浮点数(53位尾数)的正确内部使用.
Since JavaScript is an interpreted platform you should see now that there is no way to test the set of script + compiler (VM/engine) + compiler that compiled the compiler + machine in an absolute and reliable way from the point of JavaScript. So unless you want to build a test suite that acts as a browser host and actually "peeks" into the private memory of the process to ensure a valid representation, which would be fruitless most likely anyway since the number are most likely "backed" by a double and that is going to conform as it does in the C or C++ that browser was built in. There is no absolute way to do this from JavaScript since all we have access to is the "object" and even when we view the Number in a console we are looking at a .toString version. For that matter I would posit that this is the only form that matters since it will be determined from the binary and would only become a point of failure if for the statement: n1 === n2 && n1.toString() !== n2.toString() you could find an n1, n2 that is relevant...
That said, we can test the string version and in reality it is just as good as testing the binary as long as we keep a few oddities in mind. Especially since nothing outside the JavaScript engine/VM ever touches the binary version. However this puts you at the mercy of an oddly specific, possibly very finicky and poised to be changed point of failure. Just for reference, here is an excerpt from webkit's JavaScriptCore's Number Prototype (NumberPrototype.cpp) displaying the complexity of the conversion:
// The largest finite floating point number is 1.mantissa * 2^(0x7fe-0x3ff).
// Since 2^N in binary is a one bit followed by N zero bits. 1 * 2^3ff requires
// at most 1024 characters to the left of a decimal point, in base 2 (1025 if
// we include a minus sign). For the fraction, a value with an exponent of 0
// has up to 52 bits to the right of the decimal point. Each decrement of the
// exponent down to a minimum of -0x3fe adds an additional digit to the length
// of the fraction. As such the maximum fraction size is 1075 (1076 including
// a point). We pick a buffer size such that can simply place the point in the
// center of the buffer, and are guaranteed to have enough space in each direction
// fo any number of digits an IEEE number may require to represent.
typedef char RadixBuffer[2180];
// Mapping from integers 0..35 to digit identifying this value, for radix 2..36.
static const char* const radixDigits = "0123456789abcdefghijklmnopqrstuvwxyz";
static char* toStringWithRadix(RadixBuffer& buffer, double number, unsigned radix)
{
ASSERT(isfinite(number));
ASSERT(radix >= 2 && radix <= 36);
// Position the decimal point at the center of the string, set
// the startOfResultString pointer to point at the decimal point.
char* decimalPoint = buffer + sizeof(buffer) / 2;
char* startOfResultString = decimalPoint;
// Extract the sign.
bool isNegative = number < 0;
if (signbit(number))
number = -number;
double integerPart = floor(number);
// We use this to test for odd values in odd radix bases.
// Where the base is even, (e.g. 10), to determine whether a value is even we need only
// consider the least significant digit. For example, 124 in base 10 is even, because '4'
// is even. if the radix is odd, then the radix raised to an integer power is also odd.
// E.g. in base 5, 124 represents (1 * 125 + 2 * 25 + 4 * 5). Since each digit in the value
// is multiplied by an odd number, the result is even if the sum of all digits is even.
//
// For the integer portion of the result, we only need test whether the integer value is
// even or odd. For each digit of the fraction added, we should invert our idea of whether
// the number is odd if the new digit is odd.
//
// Also initialize digit to this value; for even radix values we only need track whether
// the last individual digit was odd.
bool integerPartIsOdd = integerPart <= static_cast<double>(0x1FFFFFFFFFFFFFull) && static_cast<int64_t>(integerPart) & 1;
ASSERT(integerPartIsOdd == static_cast<bool>(fmod(integerPart, 2)));
bool isOddInOddRadix = integerPartIsOdd;
uint32_t digit = integerPartIsOdd;
// Check if the value has a fractional part to convert.
double fractionPart = number - integerPart;
if (fractionPart) {
// Write the decimal point now.
*decimalPoint = '.';
// Higher precision representation of the fractional part.
Uint16WithFraction fraction(fractionPart);
bool needsRoundingUp = false;
char* endOfResultString = decimalPoint + 1;
// Calculate the delta from the current number to the next & previous possible IEEE numbers.
double nextNumber = nextafter(number, std::numeric_limits<double>::infinity());
double lastNumber = nextafter(number, -std::numeric_limits<double>::infinity());
ASSERT(isfinite(nextNumber) && !signbit(nextNumber));
ASSERT(isfinite(lastNumber) && !signbit(lastNumber));
double deltaNextDouble = nextNumber - number;
double deltaLastDouble = number - lastNumber;
ASSERT(isfinite(deltaNextDouble) && !signbit(deltaNextDouble));
ASSERT(isfinite(deltaLastDouble) && !signbit(deltaLastDouble));
// We track the delta from the current value to the next, to track how many digits of the
// fraction we need to write. For example, if the value we are converting is precisely
// 1.2345, so far we have written the digits "1.23" to a string leaving a remainder of
// 0.45, and we want to determine whether we can round off, or whether we need to keep
// appending digits ('4'). We can stop adding digits provided that then next possible
// lower IEEE value is further from 1.23 than the remainder we'd be rounding off (0.45),
// which is to say, less than 1.2255. Put another way, the delta between the prior
// possible value and this number must be more than 2x the remainder we'd be rounding off
// (or more simply half the delta between numbers must be greater than the remainder).
//
// Similarly we need track the delta to the next possible value, to dertermine whether
// to round up. In almost all cases (other than at exponent boundaries) the deltas to
// prior and subsequent values are identical, so we don't need track then separately.
if (deltaNextDouble != deltaLastDouble) {
// Since the deltas are different track them separately. Pre-multiply by 0.5.
Uint16WithFraction halfDeltaNext(deltaNextDouble, 1);
Uint16WithFraction halfDeltaLast(deltaLastDouble, 1);
while (true) {
// examine the remainder to determine whether we should be considering rounding
// up or down. If remainder is precisely 0.5 rounding is to even.
int dComparePoint5 = fraction.comparePoint5();
if (dComparePoint5 > 0 || (!dComparePoint5 && (radix & 1 ? isOddInOddRadix : digit & 1))) {
// Check for rounding up; are we closer to the value we'd round off to than
// the next IEEE value would be?
if (fraction.sumGreaterThanOne(halfDeltaNext)) {
needsRoundingUp = true;
break;
}
} else {
// Check for rounding down; are we closer to the value we'd round off to than
// the prior IEEE value would be?
if (fraction < halfDeltaLast)
break;
}
ASSERT(endOfResultString < (buffer + sizeof(buffer) - 1));
// Write a digit to the string.
fraction *= radix;
digit = fraction.floorAndSubtract();
*endOfResultString++ = radixDigits[digit];
// Keep track whether the portion written is currently even, if the radix is odd.
if (digit & 1)
isOddInOddRadix = !isOddInOddRadix;
// Shift the fractions by radix.
halfDeltaNext *= radix;
halfDeltaLast *= radix;
}
} else {
// This code is identical to that above, except since deltaNextDouble != deltaLastDouble
// we don't need to track these two values separately.
Uint16WithFraction halfDelta(deltaNextDouble, 1);
while (true) {
int dComparePoint5 = fraction.comparePoint5();
if (dComparePoint5 > 0 || (!dComparePoint5 && (radix & 1 ? isOddInOddRadix : digit & 1))) {
if (fraction.sumGreaterThanOne(halfDelta)) {
needsRoundingUp = true;
break;
}
} else if (fraction < halfDelta)
break;
ASSERT(endOfResultString < (buffer + sizeof(buffer) - 1));
fraction *= radix;
digit = fraction.floorAndSubtract();
if (digit & 1)
isOddInOddRadix = !isOddInOddRadix;
*endOfResultString++ = radixDigits[digit];
halfDelta *= radix;
}
}
// Check if the fraction needs rounding off (flag set in the loop writing digits, above).
if (needsRoundingUp) {
// Whilst the last digit is the maximum in the current radix, remove it.
// e.g. rounding up the last digit in "12.3999" is the same as rounding up the
// last digit in "12.3" - both round up to "12.4".
while (endOfResultString[-1] == radixDigits[radix - 1])
--endOfResultString;
// Radix digits are sequential in ascii/unicode, except for '9' and 'a'.
// E.g. the first 'if' case handles rounding 67.89 to 67.8a in base 16.
// The 'else if' case handles rounding of all other digits.
if (endOfResultString[-1] == '9')
endOfResultString[-1] = 'a';
else if (endOfResultString[-1] != '.')
++endOfResultString[-1];
else {
// One other possibility - there may be no digits to round up in the fraction
// (or all may be been rounded off already), in which case we may need to
// round into the integer portion of the number. Remove the decimal point.
--endOfResultString;
// In order to get here there must have been a non-zero fraction, in which case
// there must be at least one bit of the value's mantissa not in use in the
// integer part of the number. As such, adding to the integer part should not
// be able to lose precision.
ASSERT((integerPart + 1) - integerPart == 1);
++integerPart;
}
} else {
// We only need to check for trailing zeros if the value does not get rounded up.
while (endOfResultString[-1] == '0')
--endOfResultString;
}
*endOfResultString = '\0';
ASSERT(endOfResultString < buffer + sizeof(buffer));
} else
*decimalPoint = '\0';
BigInteger units(integerPart);
// Always loop at least once, to emit at least '0'.
do {
ASSERT(buffer < startOfResultString);
// Read a single digit and write it to the front of the string.
// Divide by radix to remove one digit from the value.
digit = units.divide(radix);
*--startOfResultString = radixDigits[digit];
} while (!!units);
// If the number is negative, prepend '-'.
if (isNegative)
*--startOfResultString = '-';
ASSERT(buffer <= startOfResultString);
return startOfResultString;
}
Run Code Online (Sandbox Code Playgroud)
......正如你所看到的,这里的数字是由传统的支持,double转换是简单而直接的.所以我设计的是这样的:因为我猜想这些实现的唯一不同之处在于它们对字符串的"渲染".我构建了一个三倍的测试生成器:
To accomplish this we need access to a reference build, my first thought was to use one from a native language but with that I found that the numbers produced seemed to have a higher precision than JavaScript in general leading to far more errors. So then I thought, what if I just used an implementation already inside a JavaScript engine. WebKit/JavaScriptCore seemed like a really good choice but it would have also been a lot of work to get the reference build up and running so I opted for the simplicity of .NET since it has access to "jScript" while not ideal seemed upon initial examination to produce closer results than the native counterpart. I didn't really want to code in jScript since the language is all but deprecated so I opted for C# bootstrapping jScript through a CodeDomProvider.... After a little tinkering here's what it produced: http://jsbin.com/afiqil (finally demo sauce!!!!1!), so now you can run it in all browsers and compile your own data, which upon my personal inspection it seems string rounding interpretation varies in EVERY browser I tried, however I've yet to find a major browser that handled the numbers behind the scenes (other that the stringify-ing) differently...
now for the C# sauce:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.CodeDom.Compiler;
using System.Reflection;
namespace DoubleFloatJs
{
public partial class Form1 : Form
{
private static string preamble = @"
var successes = [];
var failures = [];
function fpu_test_add(v1, v2) {
return '' + (v1 + v2);
}
function fpu_test_sub(v1, v2) {
return '' + (v1 - v2);
}
function fpu_test_mul(v1, v2) {
return '' + (v1 * v2);
}
function fpu_test_div(v1, v2) {
return '' + (v1 / v2);
}
function format(name, result1, result2, result3, received, expected) {
return '<span style=""display:inline-block;width:350px;"">' + name + '</span>' +
'<span style=""display:inline-block;width:60px;text-align:center;font-weight:bold; color:' + (result1 ? 'green;"">OK' : 'red;"">NO') + '</span>' +
'<span style=""display:inline-block;width:60px;text-align:center;font-weight:bold; color:' + (result2 ? 'green;"">OK' : 'red;"">NO') + '</span>' +
'<span style=""display:inline-block;width:60px;text-align:center;font-weight:bold; color:' + (result3 ? 'green;"">OK' : 'red;"">NO') + '</span>' +
'<span style=""display:inline-block;width:200px;vertical-align:top;"">' + received + '<br />' + expected + '</span>';
}
function check_ignore_round(received, expected) {
return received.length > 8 &&
received.length == expected.length &&
received.substr(0, received.length - 1) === expected.substr(0, expected.length - 1);
}
function check_parse_parity_no_epsilon(received, expected) {
return parseFloat(received) === parseFloat(expected);
}
function fpu_test_result(v1, v2, textFn, received, expected) {
var result = expected === received,
resultNoRound = check_ignore_round(received, expected),
resultParse = check_parse_parity_no_epsilon(received, expected),
resDiv = document.createElement('div');
resDiv.style.whiteSpace = 'nowrap';
resDiv.style.fontFamily = 'Courier New, Courier, monospace';
resDiv.style.fontSize = '0.74em';
resDiv.style.background = result ? '#aaffaa' : '#ffaaaa';
resDiv.style.borderBottom = 'solid 1px #696969';
resDiv.style.padding = '2px';
resDiv.innerHTML = format(textFn + '(' + v1 + ', ' + v2 + ')', result, resultNoRound, resultParse, received, expected);
document.body.appendChild(resDiv);
(result ? successes : failures).push(resDiv);
return resDiv;
}
function fpu_test_run(v1, v2, addRes, subRes, mulRes, divRes) {
var i, res,
fnLst = [fpu_test_add, fpu_test_sub, fpu_test_mul, fpu_test_div],
fnNam = ['add', 'sub', 'mul', 'div'];
for (i = 0; i < fnLst.length; i++) {
res = fnLst[i].call(null, v1, v2);
fpu_test_result(v1, v2, fnNam[i], res, arguments[i + 2]);
}
}
function setDisplay(s, f) {
var i;
for (i = 0; i < successes.length; i++) {
successes[i].style.display = s;
}
for (i = 0; i < failures.length; i++) {
failures[i].style.display = f;
}
}
var test_header = fpu_test_result('value1', 'value2', 'func', 'received', 'expected'),
test_header_cols = test_header.getElementsByTagName('span');
test_header_cols[1].innerHTML = 'string';
test_header_cols[2].innerHTML = 'rounded';
test_header_cols[3].innerHTML = 'parsed';
test_header.style.background = '#aaaaff';
failures.length = successes.length = 0;
";
private static string summation = @"
var bs = document.createElement('button');
var bf = document.createElement('button');
var ba = document.createElement('button');
bs.innerHTML = 'show successes (' + successes.length + ')';
bf.innerHTML = 'show failures (' + failures.length + ')';
ba.innerHTML = 'show all (' + (successes.length + failures.length) + ')';
ba.style.width = bs.style.width = bf.style.width = '200px';
ba.style.margin = bs.style.margin = bf.style.margin = '4px';
ba.style.padding = bs.style.padding = bf.style.padding = '4px';
bs.onclick = function() { setDisplay('block', 'none'); };
bf.onclick = function() { setDisplay('none', 'block'); };
ba.onclick = function() { setDisplay('block', 'block'); };
document.body.insertBefore(bs, test_header);
document.body.insertBefore(bf, test_header);
document.body.insertBefore(ba, test_header);
document.body.style.minWidth = '700px';
";
private void buttonGenerate_Click(object sender, EventArgs e)
{
var numberOfTests = this.numericNumOfTests.Value;
var strb = new StringBuilder(preamble);
var rand = new Random();
for (int i = 0; i < numberOfTests; i++)
{
double v1 = rand.NextDouble();
double v2 = rand.NextDouble();
strb.Append("fpu_test_run(")
.Append(v1)
.Append(", ")
.Append(v2)
.Append(", '")
.Append(JsEval("" + v1 + '+' + v2))
.Append("', '")
.Append(JsEval("" + v1 + '-' + v2))
.Append("', '")
.Append(JsEval("" + v1 + '*' + v2))
.Append("', '")
.Append(JsEval("" + v1 + '/' + v2))
.Append("');")
.AppendLine();
}
strb.Append(summation);
this.textboxOutput.Text = strb.ToString();
Clipboard.SetText(this.textboxOutput.Text);
}
public Form1()
{
InitializeComponent();
Type evalType = CodeDomProvider
.CreateProvider("JScript")
.CompileAssemblyFromSource(new CompilerParameters(), "package e{class v{public static function e(e:String):String{return eval(e);}}}")
.CompiledAssembly
.GetType("e.v");
this.JsEval = s => (string)evalType.GetMethod("e").Invoke(null, new[] { s });
}
private readonly Func<string, string> JsEval;
}
}
Run Code Online (Sandbox Code Playgroud)
or a pre-compiled version if you should choose: http://uploading.com/files/ad4a85md/DoubleFloatJs.exe/ this is an executable, download at your own risk

I should mention that the purpose of the program is just to produce a JavaScript file in a text box and copy it to the clipboard for convenience for pasting wherever you choose, you could easily turn this around and put it on an asp.net server and add reporting to results to ping the server and keep track in some massive database... which is what I would do to it if I desired the information..
...and, ...I'm, ...spent I hope this helps you -ck
总结下面的所有内容,您可以预期大多数系统的合规性,除了一些IE的故障,但应该使用健全性检查作为预防措施(包括命题).
ECMAScript规范(第3版和第5版)在IEEE 754的特性上非常精确,例如转换,舍入模式,上溢/下溢甚至有符号零(第5.2,8.5,9.*,11.5.*,11.8.5; 15.7和15.8也处理浮子).它不仅仅是"将事情留给底层实现".vv之间没有明显的差异.3和5以及所有主流浏览器至少支持v.3.所以每个人都尊重它的规则,至少在名义上.让我们来看看...
没有浏览器完全通过test262 ECMAScript一致性测试(WP#一致性测试).但是,谷歌上没有发现 test262错误与浮动相关.
IE5.5 +([MS-ES3] )中报道的差异Number.toFixed,Number.toExponential,Number.toPrecision.其他差异与浮动无关.我无法在IE8中运行test262;
要验证系统,可以使用test262中与浮点相关的测试.他们位于http://test262.ecmascript.org/json/ch<2-digit # of spec chapter>.json; 测试代码可以用(python 2.6+)提取:
ch="05"; #substitute chapter #
import urllib,json,base64
j=json.load(urllib.urlopen("http://test262.ecmascript.org/json/ch%s.json"%ch))
tt=j['testsCollection']['tests']
f=open('ch%s.js'%ch,'w')
for t in tt:
print >>f
print >>f,base64.b64decode(t['code'])
f.close()
Run Code Online (Sandbox Code Playgroud)
另一个机会是C中的IEEE 754一致性测试.
test262的相关部分(比较浮点数的部分)如下:
{
"S11": "5.1.A4: T1-T8",
"S15": {
"7": "3: 2.A1 & 3.A1",
"8": {
"1": "1-8: A1",
"2": {
"4": "A4 & A5",
"5": "A: 3,6,7,10-13,15,17-19",
"7": "A6 & A7",
"13": "A24",
"16": "A6 & A7",
"17": "A6",
"18": "A6"
}
}
},
"S8": "5.A2: 1 & 2"
}
Run Code Online (Sandbox Code Playgroud)
这个列表和所有相关测试文件的连接源(截至2012年3月9日,没有来自线束的文件)可以在这里找到:http://pastebin.com/U6nX6sKL
一般经验法则是,当数字精度很重要并且您只能访问浮点精度数时,所有计算都应该作为整数数学来完成,以最好地确保有效性(确保有效数据的15位数).是的,JavaScript中有一堆通用的数字特性,但它们与浮点数内缺乏精确性有关,而与标准的UA实现无关.环顾浮点数学的陷阱,它们数不胜数而且背信弃义.
我觉得我应该详细说明一下,比如我编写了一个程序(用JavaScript),它使用基本的微积分来确定多边形的面积,尺寸以米或英尺为单位.程序不是按原样进行计算,而是将所有内容转换为微米并在那里进行计算,因为一切都会更加完整.
希望这有助于确定
回应您的澄清,评论和关注
我不打算在下面重复我的评论,但简短的回答是,没有人能够说100%的设备上的每一个实现都是100%.期.我可以说和其他人会告诉你一样,是在当前的主流浏览器上我还没有看到也没有听说任何涉及浮点数的浏览器特定的有害bug.但是你的问题本身就是一把双刃剑,因为你想"依赖""不可靠"的结果,或者仅仅是你希望所有的浏览器"始终不一致" - 换句话说,而不是试图确保狮子会玩取,你的时间会更好地寻找一只狗,意思是:你可以依靠110%的整数数学和所说的数学结果,同样适用于已经建议你的字符串数学......
祝你好运
“我对分布式 Web 应用程序有一个特定的用途,只有当我可以在所有浏览器上获得一致的结果时,它才有效。”
那么答案是否定的。您不能依赖规范来告诉您浏览器正确处理浮动。Chrome 每 6 周更新一次,因此即使您有规范,Chrome 也可能会在下一版本中更改其行为。
您必须在每次运行计算之前依赖功能测试来测试您的假设。
(编辑:下面提到的错误已于 2016 年 3 月 3 日修复并关闭。所以我的答案现在是“也许”。)
不幸的是答案是否定的。v8 中至少存在一个突出的错误,由于双舍入,这意味着它可能与 32 位 Linux 上的 IEEE 754 双精度不匹配。
这可以通过以下方式进行测试:
9007199254740994 + 0.99999 === 9007199254740994
Run Code Online (Sandbox Code Playgroud)
9007199254740996我可以验证在 32 位 Ubuntu 上运行的 Chrome 26.0.1410.63 上此操作是否失败(左侧为)。它在同一系统上传递了 Firefox 20.0。至少,这个测试应该添加到您的测试套件中,也许是 test262。