简体   繁体   English

JavaScript toFixed()无法按预期工作

[英]Javascript toFixed() is not working as expected

I am using toFixed but the method does not operate as expected 我正在使用toFixed但是该方法无法按预期运行

parseFloat(19373.315).toFixed(2);

//19373.31   Chrome 

Expected Output : 19373.32 预期产量:19373.32

parseFloat(9373.315).toFixed(2);
// 9373.32  Working fine

Why does the first example round down, whereas the second example round up? 为什么第一个示例向下取整,而第二个示例向上取整?

The problem is that binary floating point representation of most decimal fractions is not exact. 问题在于大多数小数部分的二进制浮点表示形式不准确。 The internal representation of 19373.315 may actually be something like 19373.314999999, so toFixed rounds down, while 19373.315 might be 19373.315000001, which rounds up. 19373.315的内部表示实际上可能类似于19373.314999999,因此toFixed向下取整,而19373.315可能为19373.315000001,向上取整。

Why does the first example round down, whereas the second example round up? 为什么第一个示例向下取整,而第二个示例向上取整?

Look at the binary representation of the two values in memory. 查看内存中两个值的二进制表示形式。

 const farr = new Float64Array(2); farr[0] = 19373.315; farr[1] = 9373.315; const uarr = new Uint32Array(farr.buffer); console.log(farr[0], uarr[1].toString(2).padStart(32, 0) + uarr[0].toString(2).padStart(32, 0)); console.log(farr[1], uarr[3].toString(2).padStart(32, 0) + uarr[2].toString(2).padStart(32, 0)); 

Without diving into the details, we can see that the second value has an additional '1' at the end, which is lost in the first larger value when it is fit into 64 bits. 在不深入细节的情况下,我们可以看到第二个值的末尾有一个额外的“ 1”,当它适合64位时,会在第一个较大的值中丢失。

Assuming toFixed casts to 32-bit float; 假设固定转换为32位浮点型; Check with this utility... 检查此实用程序...

19373.315 is stored as 19373.314453125 (an error of -0.000546875) in 32-bit floating point format. 19373.315以32位浮点格式存储为19373.314453125(错误-0.000546875)。

This is despite (19373.315).toFixed(4) coming out as 19373.3150 . 尽管(19373.315).toFixed(4)结果为19373.3150

Even if this is "expected" or "intended", I'd still report it as a bug. 即使这是“预期的”或“预期的”,我仍将其报告为错误。

It should use a double during the rounding check, and thus proper rounding during conversion to fixed string. 在四舍五入检查期间应使用双精度,因此在转换为固定字符串期间应进行适当的四舍五入。

I think the spec even says so. 我认为规范甚至是这样说的。 :\\ :\\

In the V8 javascript engine source, the Number.prototype.toFixed function invokes DoubleToFixedCString in this file ... 在V8 javascript引擎源代码中, Number.prototype.toFixed函数在此文件中调用DoubleToFixedCString ...

There's probably some inappropriate optimization in there... (Looking into it.) 那里可能有一些不适当的优化...(正在研究它。)

I'd suggest submitting an additional test case for V8 with 19373.315 specifically. 我建议为V8额外提交一个测试案例,具体 19373.315。

(19373.3150).toFixed(39) yields 19373.314999999998690327629446983337402343750. (19373.3150).toFixed(39)得出19373.314999999998690327629446983337402343750。

Rounding occurs once to bring it up to 19373.315 - which is correct - but not at the right digit when rounding to 2 digits. 舍入一次会使其达到19373.315(这是正确的),但是舍入到两位数时不会出现在正确的数字上。

I think this should have a second pass on rounding here to catch edge cases like this. 我认为这里应该进行第二次四舍五入,以捕获像这样的边缘情况。 I think it might have to round to n+1 digits, then again to n digits. 我想可能有舍入到n+1再次数字,然后n数字。 Maybe there's some other clever way to fix it though. 也许还有其他一些聪明的方法可以解决它。

 function toFixedFixed(a,n) { return (a|0) + parseFloat((a % 1).toFixed(n+1)).toFixed(n).substr(1); } console.log(toFixedFixed(19373.315,2)); // "19373.32" console.log(toFixedFixed(19373.315,3)); // "19373.315" console.log(toFixedFixed(19373.315,4)); // "19373.3150" console.log(toFixedFixed(19373.315,37)); // "19373.3149999999986903276294469833374023438" console.log(toFixedFixed(19373.315,38)); // "19373.31499999999869032762944698333740234375" console.log(toFixedFixed(19373.315,39)); // "19373.314999999998690327629446983337402343750" 

(Adopted from my comments on Vahid Rahmani's answer, who is correct.) (摘自我对Vahid Rahmani的回答的评论,谁是正确的。)

其他答案也解释了为什么,我建议使用像numeric.js这样的库,它将按您期望的那样处理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM