在javascript中截断(不四舍五入)十进制数

IT技术 javascript floating-point floating-accuracy
2021-01-14 07:39:26

我正在尝试将十进制数截断为小数位。像这样的东西:

5.467   -> 5.46  
985.943 -> 985.94

toFixed(2)做了几乎正确的事情,但它使value四舍五入。我不需要四舍五入的值。希望这在javascript中是可能的。

6个回答

Dogbert 的回答很好,但如果您的代码可能必须处理负数,则Math.floor其本身可能会产生意想不到的结果。

例如Math.floor(4.3) = 4,但是Math.floor(-4.3) = -5

使用类似这样的辅助函数来获得一致的结果:

truncateDecimals = function (number) {
    return Math[number < 0 ? 'ceil' : 'floor'](number);
};

// Applied to Dogbert's answer:
var a = 5.467;
var truncated = truncateDecimals(a * 100) / 100; // = 5.46

这是这个函数的一个更方便的版本:

truncateDecimals = function (number, digits) {
    var multiplier = Math.pow(10, digits),
        adjustedNum = number * multiplier,
        truncatedNum = Math[adjustedNum < 0 ? 'ceil' : 'floor'](adjustedNum);

    return truncatedNum / multiplier;
};

// Usage:
var a = 5.467;
var truncated = truncateDecimals(a, 2); // = 5.46

// Negative digits:
var b = 4235.24;
var truncated = truncateDecimals(b, -2); // = 4200

如果这不是我们想要的行为,请Math.abs在第一行插入一个调用

var multiplier = Math.pow(10, Math.abs(digits)),

编辑: shendz 正确地指出,使用此解决方案 witha = 17.56将错误地生成17.55. 有关为什么会发生这种情况的更多信息,请阅读每个计算机科学家应该知道的关于浮点运算的知识不幸的是,使用 javascript 编写一个消除所有浮点错误来源的解决方案非常棘手。在另一种语言中,您将使用整数或十进制类型,但使用 javascript ......

这个解决方案应该是100% 准确的,但它也会变慢:

function truncateDecimals (num, digits) {
    var numS = num.toString(),
        decPos = numS.indexOf('.'),
        substrLength = decPos == -1 ? numS.length : 1 + decPos + digits,
        trimmedResult = numS.substr(0, substrLength),
        finalResult = isNaN(trimmedResult) ? 0 : trimmedResult;

    return parseFloat(finalResult);
}

对于那些需要速度但又想避免浮点错误的人,可以试试BigDecimal.js 之类的东西您可以在这个 SO 问题中找到其他 javascript BigDecimal 库:“是否有一个好的 Javascript BigDecimal 库?” 这是一篇关于Javascript 数学库的好博文

它完全符合它所说的 - 当您想要截断小数而不是四舍五入时。
2021-03-20 07:39:26
好点shendz。我已经用一个解决方案更新了我的答案,该解决方案为那些需要的人消除了所有浮点错误。
2021-03-28 07:39:26
如果您不想要小数,这不适用于小于 1 的数字 - truncateDecimals(.12345, 0) 结果为 NaN 除非您添加检查: if(isNAN(result) result = 0; 取决于您想要的行为。
2021-03-29 07:39:26
为什么出乎意料?当你低于 0 时改变四舍五入的方向,会导致各种算术伪像和糟糕的数学。例如,将舍入为 0 的数字是任何其他整数的两倍。对于图形、会计和许多其他用途,您会得到糟糕的结果。说句实话,这将会是很难说你的建议是什么如说什么这是不是
2021-04-10 07:39:26
不适用于 17.56,因为浏览器给出 17.56 * 100 = 1755.9999999999998 而不是 1756
2021-04-11 07:39:26

更新

所以,毕竟事实证明,舍入错误总是困扰着你,无论你如何努力补偿它们。因此,应该通过精确地用十进制表示法来表示数字来解决这个问题。

Number.prototype.toFixedDown = function(digits) {
    var re = new RegExp("(\\d+\\.\\d{" + digits + "})(\\d)"),
        m = this.toString().match(re);
    return m ? parseFloat(m[1]) : this.valueOf();
};

[   5.467.toFixedDown(2),
    985.943.toFixedDown(2),
    17.56.toFixedDown(2),
    (0).toFixedDown(1),
    1.11.toFixedDown(1) + 22];

// [5.46, 985.94, 17.56, 0, 23.1]

基于其他人的编译的旧的容易出错的解决方案:

Number.prototype.toFixedDown = function(digits) {
  var n = this - Math.pow(10, -digits)/2;
  n += n / Math.pow(2, 53); // added 1360765523: 17.56.toFixedDown(2) === "17.56"
  return n.toFixed(digits);
}
请注意,此函数会删除负号。例如:(-10.2131).toFixedDown(2) // ==> 10.21
2021-03-14 07:39:26
这不能正常工作。试试数字 17.56 和数字 = 2。它应该是 17.56,但这个函数返回 17.55。
2021-03-25 07:39:26
还有,(1e-7).toFixedDown(0) // ==> 1e-7是否适用于1e-(>=7)(例如:1e-8, 1e-9, ...)。
2021-03-31 07:39:26
是的,原型不能可靠地跨浏览器工作。与其通过类型系统以一种不可靠的方式定义这个(有限用途的)函数,为什么不把它放在一个库中。
2021-04-04 07:39:26
此函数有两个不一致之处:此函数返回一个字符串,因此1.11.toFixedDown(1) + 22以 as1.122而不是23.1. 0.toFixedDown(1)应该产生0但它产生-0.1.
2021-04-12 07:39:26
var a = 5.467;
var truncated = Math.floor(a * 100) / 100; // = 5.46
这很有效,但如果他(或稍后查看此答案的其他人)必须处理负数,则会产生可能不受欢迎的结果。参见stackoverflow.com/a/9232092/224354
2021-03-17 07:39:26
var a = 65.1 var truncated = Math.floor(a * 100) / 100; // = 65.09 因此这不是一个正确的解决方案
2021-03-25 07:39:26
为什么不受欢迎?当您低于 0 时改变舍入方向,会导致各种算术伪影。
2021-04-02 07:39:26
我同意托马斯。无论您通常是为了显示还是为了计算而截断,透视图的差异都可能存在。从计算的角度来看,这避免了“算术伪影”
2021-04-09 07:39:26
舍入和截断是有区别的。截断显然是这个问题所寻求的行为。如果我打电话truncate(-3.14)并收到-4回复,我肯定会称那是不受欢迎的。
2021-04-11 07:39:26

您可以通过为 toFixed 减去 0.5 来修复舍入,例如

(f - 0.005).toFixed(2)
注意:事实上,这不适用于非常小的数字、超过 3 位小数的数字或负数。尝试 .0045、5.4678 和 -5.467
2021-04-01 07:39:26
只要您将减去的值与您希望拥有的长度相匹配,这就会起作用。您传递给 toFixed() 的任何内容都需要是小数点后的 0 数。
2021-04-13 07:39:26

考虑利用双波浪线:~~ .

取号。乘以小数点后的有效数字,以便您可以使用截断到零位~~将该乘数除掉。利润。

function truncator(numToTruncate, intDecimalPlaces) {    
    var numPower = Math.pow(10, intDecimalPlaces); // "numPowerConverter" might be better
    return ~~(numToTruncate * numPower)/numPower;
}

我试图抵制~~用括号包装调用;我相信,操作顺序应该使其正常工作。

alert(truncator(5.1231231, 1)); // is 5.1

alert(truncator(-5.73, 1)); // is -5.7

alert(truncator(-5.73, 0)); // is -5

JSFiddle 链接

编辑:回顾过去,我无意中也处理了小数点左边四舍五入的情况。

alert(truncator(4343.123, -2)); // gives 4300.

寻找这种用法的逻辑有点古怪,并且可能会从快速重构中受益。但它仍然有效。好运不如好运。

truncator((10 * 2.9) / 100, 2)返回 0.28 而不是 0.29 ... jsfiddle.net/25tgrzq1
2021-03-18 07:39:26
对我不起作用,截断器(1000.12345678, 7) 返回 141.1299975
2021-03-18 07:39:26
@Alex 如果要避免浮点错误,请使用小数类型或者我想我们可以重写以使用字符串操作,但这似乎很疯狂。你会注意到类似的答案有同样的问题。
2021-03-22 07:39:26
@HelpfulPanda 那是因为JavaScript 使用 32 位整数作为按位运算符最多32位int为2,147,483,647,而且100012345678比显著更大2147483647如果您确实有大于 32 位的数字(相反,如果您需要那么多有效数字),那么这不是您要寻找的机器人。这是一个快速而肮脏(且快速)的答案,例如 4-5 个 sig 数字。对于潜在的矫枉过正的完美,请尝试接受的答案;^D
2021-03-30 07:39:26
这是最好的答案。如果你Math用这个扩展原型并在执行之前检查 NaN-s,那将是完美的。
2021-04-04 07:39:26