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

kcs*_*ssm 76 javascript floating-point floating-accuracy

我试图将十进制数截断为小数位.像这样的东西:

5.467   -> 5.46  
985.943 -> 985.94
Run Code Online (Sandbox Code Playgroud)

toFixed(2)做正确的事情,但它完成了价值.我不需要四舍五入的值.希望这在javascript中是可能的.

Nic*_*son 52

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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

如果这不是所需的行为,请Math.abs在第一行插入呼叫:

var multiplier = Math.pow(10, Math.abs(digits)),
Run Code Online (Sandbox Code Playgroud)

编辑: shendz正确指出使用此解决方案a = 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);
}
Run Code Online (Sandbox Code Playgroud)

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


kir*_*oid 47

更新:

所以,事实证明,无论你多么努力地补偿它们,四舍五入的错误总会困扰着你.因此,应该通过以十进制表示法精确表示数字来攻击该问题.

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]
Run Code Online (Sandbox Code Playgroud)

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

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);
}
Run Code Online (Sandbox Code Playgroud)

  • 此外,`(1e-7).toFixedDown(0) // ==&gt; 1e-7`。是否对`1e-(&gt;=7)`(例如:`1e-8`、`1e-9`、...)执行此操作。 (4认同)
  • 这不是例外.尝试使用数字17.56和数字= 2.它应该是17.56,但是此函数返回17.55. (3认同)
  • 请注意,此功能会删除负号.例如:`( - 10.2131).toFixedDown(2)// ==> 10.21`. (3认同)
  • 是的,Prototypes无法跨浏览器可靠地工作.不是通过类型系统定义这个(有限目的)函数,而是以不可靠的方式工作,为什么不把它放在库中. (2认同)
  • 这个函数有两个不一致:这个函数返回一个字符串,所以`1.11.toFixedDown(1)+ 22`最终为`1.122`而不是`23.1`.同样``0.toFixedDown(1)`应该产生`0`,但它产生`-0.1`. (2认同)

Dog*_*ert 31

var a = 5.467;
var truncated = Math.floor(a * 100) / 100; // = 5.46
Run Code Online (Sandbox Code Playgroud)

  • 舍入和截断之间存在差异.截断显然是这个问题正在寻求的行为.如果我调用`truncate(-3.14)`并接收`-4`,我肯定会称之为不合需要的. (7认同)
  • 这种方法效果很好,但如果他(或其他人稍后查看此答案)必须处理负数,则会产生可能不合适的结果.请参见http://stackoverflow.com/a/9232092/224354 (5认同)
  • `var a = 65.1` `var truncated = Math.floor(a * 100) / 100; // = 65.09` 因此这不是一个正确的解决方案 (3认同)
  • 为何不受欢迎 当你低于0时改变舍入的方向,会导致各种算术伪影. (2认同)

Ric*_*iwi 21

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

(f - 0.005).toFixed(2)
Run Code Online (Sandbox Code Playgroud)


ruf*_*fin 16

考虑利用双重波形:~~.

记下号码.乘以小数点后的有效数字,以便您可以截断到零位~~.将乘数除去.利润.

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

我试图抵制~~在parens 包裹呼叫; 我相信,操作的顺序应该能够正常工作.

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.

逻辑有点古怪,寻找这种用法,并可能从快速重构中受益.但它仍然有效.比幸运更好.


Dan*_*ore 10

我以为我会用一个答案,|因为它简单而且效果很好.

truncate = function(number, places) {
  var shift = Math.pow(10, places);

  return ((number * shift) | 0) / shift;
};
Run Code Online (Sandbox Code Playgroud)


小智 9

不错的单线解决方案:

function truncate (num, places) {
  return Math.trunc(num * Math.pow(10, places)) / Math.pow(10, places);
}
Run Code Online (Sandbox Code Playgroud)

然后用:

truncate(3.5636232, 2); // returns 3.56
truncate(5.4332312, 3); // returns 5.433
truncate(25.463214, 4); // returns 25.4632
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢这个解决方案,但请记住,并非所有浏览器都完全支持它.(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc#Browser_compatibility) (2认同)

zur*_*fyx 6

@ Dogbert的答案可以改进Math.trunc,截断而不是舍入.

舍入和截断之间存在差异.截断显然是这个问题正在寻求的行为.如果我调用truncate(-3.14)并接收-4返回,我肯定会称之为不合需要的. - @NickKnowlson

var a = 5.467;
var truncated = Math.trunc(a * 100) / 100; // = 5.46
Run Code Online (Sandbox Code Playgroud)
var a = -5.467;
var truncated = Math.trunc(a * 100) / 100; // = -5.46
Run Code Online (Sandbox Code Playgroud)


小智 6

我用更短的方法写了一个答案。这是我想出的

function truncate(value, precision) {
    var step = Math.pow(10, precision || 0);
    var temp = Math.trunc(step * value);

    return temp / step;
}
Run Code Online (Sandbox Code Playgroud)

该方法可以像这样使用

truncate(132456.25456789, 5)); // Output: 132456.25456
truncate(132456.25456789, 3)); // Output: 132456.254
truncate(132456.25456789, 1)); // Output: 132456.2   
truncate(132456.25456789));    // Output: 132456
Run Code Online (Sandbox Code Playgroud)

或者,如果你想要一个更短的语法,就在这里

function truncate(v, p) {
    var s = Math.pow(10, p || 0);
    return Math.trunc(s * v) / s;
}
Run Code Online (Sandbox Code Playgroud)


Joh*_*ler 5

使用按位运算符截断:

~~0.5 === 0
~~(-0.5) === 0
~~14.32794823 === 14
~~(-439.93) === -439
Run Code Online (Sandbox Code Playgroud)


Jul*_*nez 5

我认为这个功能可能是一个简单的解决方案:

function trunc(decimal,n=2){
  let x = decimal + ''; // string 
  return x.lastIndexOf('.')>=0?parseFloat(x.substr(0,x.lastIndexOf('.')+(n+1))):decimal; // You can use indexOf() instead of lastIndexOf()
}

console.log(trunc(-241.31234,2));
console.log(trunc(241.312,5));
console.log(trunc(-241.233));
console.log(trunc(241.2,0));  
console.log(trunc(241));
Run Code Online (Sandbox Code Playgroud)