将数字截断为两位小数而不进行舍入

问题描述 投票:162回答:32

假设我的值为15.7784514,我希望将其显示为15.77而不进行舍入。

var num = parseFloat(15.7784514);
document.write(num.toFixed(1)+"<br />");
document.write(num.toFixed(2)+"<br />");
document.write(num.toFixed(3)+"<br />");
document.write(num.toFixed(10));

结果是 -

15.8
15.78
15.778
15.7784514000 

我如何显示15.77?

javascript
32个回答
199
投票

将数字转换为字符串,将数字与第二个小数位匹配:

function calc(theform) {
    var num = theform.original.value, rounded = theform.rounded
    var with2Decimals = num.toString().match(/^-?\d+(?:\.\d{0,2})?/)[0]
    rounded.value = with2Decimals
}
<form onsubmit="return calc(this)">
Original number: <input name="original" type="text" onkeyup="calc(form)" onchange="calc(form)" />
<br />"Rounded" number: <input name="rounded" type="text" placeholder="readonly" readonly>
</form>

toFixed方法在某些情况下与toString不同,所以要非常小心。


4
投票

这些解决方案确实有效,但对我来说似乎不必要地复杂化。我个人喜欢使用模运算符来获得除法运算的剩余部分,并删除它。假设num = 15.7784514:

num-=num%.01;

这相当于说num = num - (num%.01)。


4
投票

以下代码对我非常有用:

num.toString().match(/.\*\\..{0,2}|.\*/)[0];

3
投票

我使用以下简单方法修复 -

var num = 15.7784514;
Math.floor(num*100)/100;

结果将是15.77


2
投票

这个给你。答案显示了另一种解决问题的方法:

// For the sake of simplicity, here is a complete function:
function truncate(numToBeTruncated, numOfDecimals) {
    var theNumber = numToBeTruncated.toString();
    var pointIndex = theNumber.indexOf('.');
    return +(theNumber.slice(0, pointIndex > -1 ? ++numOfDecimals + pointIndex : undefined));
}

注意在最终表达式之前使用+。那就是将截断的切片字符串转换回数字类型。

希望能帮助到你!


2
投票

截断没有零

function toTrunc(value,n){  
    return Math.floor(value*Math.pow(10,n))/(Math.pow(10,n));
}

要么

function toTrunc(value,n){
    x=(value.toString()+".0").split(".");
    return parseFloat(x[0]+"."+x[1].substr(0,n));
}

测试:

toTrunc(17.4532,2)  //17.45
toTrunc(177.4532,1) //177.4
toTrunc(1.4532,1)   //1.4
toTrunc(.4,2)       //0.4

用零截断

function toTruncFixed(value,n){
    return toTrunc(value,n).toFixed(n);
}

测试:

toTrunc(17.4532,2)  //17.45
toTrunc(177.4532,1) //177.4
toTrunc(1.4532,1)   //1.4
toTrunc(.4,2)       //0.40

2
投票

一个简单的方法是下一步,但必须确保amount参数以字符串形式给出。

function truncate(amountAsString, decimals = 2){
  var dotIndex = amountAsString.indexOf('.');
  var toTruncate = dotIndex !== -1  && ( amountAsString.length > dotIndex + decimals + 1);
  var approach = Math.pow(10, decimals);
  var amountToTruncate = toTruncate ? amountAsString.slice(0, dotIndex + decimals +1) : amountAsString;  
  return toTruncate
    ?  Math.floor(parseFloat(amountToTruncate) * approach ) / approach
    :  parseFloat(amountAsString);

}

console.log(truncate("7.99999")); //OUTPUT ==> 7.99
console.log(truncate("7.99999", 3)); //OUTPUT ==> 7.999
console.log(truncate("12.799999999999999")); //OUTPUT ==> 7.99

1
投票

我使用(num-0.05).toFixed(1)得到第二个小数点。


1
投票

另一个单线解决方案:

number = Math.trunc(number*100)/100

我使用100因为你想要截断到第二个数字,但更灵活的解决方案是:

number = Math.trunc(number*Math.pow(10, digits))/Math.pow(10, digits)

其中digits是要保留的小数位数。


1
投票

这对我很有用。我希望它也能解决你的问题。

function toFixedNumber(number) {
    const spitedValues = String(number.toLocaleString()).split('.');
    let decimalValue = spitedValues.length > 1 ? spitedValues[1] : '';
    decimalValue = decimalValue.concat('00').substr(0,2);

    return '$'+spitedValues[0] + '.' + decimalValue;
}

// 5.56789      ---->  $5.56
// 0.342        ---->  $0.34
// -10.3484534  ---->  $-10.34 
// 600          ---->  $600.00

function convertNumber(){
  var result = toFixedNumber(document.getElementById("valueText").value);
  document.getElementById("resultText").value = result;
}


function toFixedNumber(number) {
        const spitedValues = String(number.toLocaleString()).split('.');
        let decimalValue = spitedValues.length > 1 ? spitedValues[1] : '';
        decimalValue = decimalValue.concat('00').substr(0,2);

        return '$'+spitedValues[0] + '.' + decimalValue;
}
<div>
  <input type="text" id="valueText" placeholder="Input value here..">
  <br>
  <button onclick="convertNumber()" >Convert</button>
  <br><hr>
  <input type="text" id="resultText" placeholder="result" readonly="true">
</div>

1
投票

只是截断数字:

function truncDigits(inputNumber, digits) {
  const fact = 10 ** digits;
  return Math.floor(inputNumber * fact) / fact;
}

55
投票

Update 5 Nov 2016

新答案,始终准确

function toFixed(num, fixed) {
    var re = new RegExp('^-?\\d+(?:\.\\d{0,' + (fixed || -1) + '})?');
    return num.toString().match(re)[0];
}

由于floating point math in javascript总是有边缘情况,以前的解决方案在大多数时间都是准确的,这还不够好。有一些解决方案,如num.toPrecisionBigDecimal.jsaccounting.js。然而,我相信仅仅解析字符串将是最简单且始终准确的。

基于@Gumbo接受的答案中写得好的正则表达式的更新,这个新的toFixed函数将始终按预期工作。


老答案,并不总是准确的。

滚动你自己的固定功能:

function toFixed(num, fixed) {
    fixed = fixed || 0;
    fixed = Math.pow(10, fixed);
    return Math.floor(num * fixed) / fixed;
}

0
投票

滚动你自己的toFixed功能:对于正值Math.floor工作正常。

function toFixed(num, fixed) {
    fixed = fixed || 0;
    fixed = Math.pow(10, fixed);
    return Math.floor(num * fixed) / fixed;
}

对于负值,Math.floor是值的一部分。所以你可以使用Math.ceil代替。

例,

Math.ceil(-15.778665 * 10000) / 10000 = -15.7786
Math.floor(-15.778665 * 10000) / 10000 = -15.7787 // wrong.

0
投票

Gumbo的第二个解决方案,使用正则表达式,确实有效,但由于正则表达式,它很慢。由于浮点数不精确,Gumbo的第一个解决方案在某些情况下失败。请参阅JSFiddle进行演示和基准测试。第二种解决方案在我当前的系统上每次呼叫大约需要1636纳秒,即3.30 GHz的英特尔酷睿i5-2500 CPU。

我写的解决方案涉及添加一个小补偿来处理浮点不精确。它基本上是瞬时的,即在纳秒级。我每次调用时钟为2纳秒,但JavaScript定时器不是非常精确或粒度。这是JS Fiddle和代码。

function toFixedWithoutRounding (value, precision)
{
    var factorError = Math.pow(10, 14);
    var factorTruncate = Math.pow(10, 14 - precision);
    var factorDecimal = Math.pow(10, precision);
    return Math.floor(Math.floor(value * factorError + 1) / factorTruncate) / factorDecimal;
}

var values = [1.1299999999, 1.13, 1.139999999, 1.14, 1.14000000001, 1.13 * 100];

for (var i = 0; i < values.length; i++)
{
    var value = values[i];
    console.log(value + " --> " + toFixedWithoutRounding(value, 2));
}

for (var i = 0; i < values.length; i++)
{
    var value = values[i];
    console.log(value + " --> " + toFixedWithoutRounding(value, 4));
}

console.log("type of result is " + typeof toFixedWithoutRounding(1.13 * 100 / 100, 2));

// Benchmark
var value = 1.13 * 100;
var startTime = new Date();
var numRun = 1000000;
var nanosecondsPerMilliseconds = 1000000;

for (var run = 0; run < numRun; run++)
    toFixedWithoutRounding(value, 2);

var endTime = new Date();
var timeDiffNs = nanosecondsPerMilliseconds * (endTime - startTime);
var timePerCallNs = timeDiffNs / numRun;
console.log("Time per call (nanoseconds): " + timePerCallNs);

0
投票

以David D的答案为基础:

function NumberFormat(num,n) {
  var num = (arguments[0] != null) ? arguments[0] : 0;
  var n = (arguments[1] != null) ? arguments[1] : 2;
  if(num > 0){
    num = String(num);
    if(num.indexOf('.') !== -1) {
      var numarr = num.split(".");
      if (numarr.length > 1) {
        if(n > 0){
          var temp = numarr[0] + ".";
          for(var i = 0; i < n; i++){
            if(i < numarr[1].length){
              temp += numarr[1].charAt(i);
            }
          }
          num = Number(temp);
        }
      }
    }
  }
  return Number(num);
}

console.log('NumberFormat(123.85,2)',NumberFormat(123.85,2));
console.log('NumberFormat(123.851,2)',NumberFormat(123.851,2));
console.log('NumberFormat(0.85,2)',NumberFormat(0.85,2));
console.log('NumberFormat(0.851,2)',NumberFormat(0.851,2));
console.log('NumberFormat(0.85156,2)',NumberFormat(0.85156,2));
console.log('NumberFormat(0.85156,4)',NumberFormat(0.85156,4));
console.log('NumberFormat(0.85156,8)',NumberFormat(0.85156,8));
console.log('NumberFormat(".85156",2)',NumberFormat(".85156",2));
console.log('NumberFormat("0.85156",2)',NumberFormat("0.85156",2));
console.log('NumberFormat("1005.85156",2)',NumberFormat("1005.85156",2));
console.log('NumberFormat("0",2)',NumberFormat("0",2));
console.log('NumberFormat("",2)',NumberFormat("",2));
console.log('NumberFormat(85156,8)',NumberFormat(85156,8));
console.log('NumberFormat("85156",2)',NumberFormat("85156",2));
console.log('NumberFormat("85156.",2)',NumberFormat("85156.",2));

// NumberFormat(123.85,2) 123.85
// NumberFormat(123.851,2) 123.85
// NumberFormat(0.85,2) 0.85
// NumberFormat(0.851,2) 0.85
// NumberFormat(0.85156,2) 0.85
// NumberFormat(0.85156,4) 0.8515
// NumberFormat(0.85156,8) 0.85156
// NumberFormat(".85156",2) 0.85
// NumberFormat("0.85156",2) 0.85
// NumberFormat("1005.85156",2) 1005.85
// NumberFormat("0",2) 0
// NumberFormat("",2) 0
// NumberFormat(85156,8) 85156
// NumberFormat("85156",2) 85156
// NumberFormat("85156.",2) 85156

0
投票

获得两个没有舍入的浮点更可靠。

Reference Answer

var number = 10.5859;
var fixed2FloatPoints = parseInt(number * 100) / 100;
console.log(fixed2FloatPoints);

谢谢 !


0
投票

已经有正则表达式和算术计算的一些合适的答案,你也可以试试这个

function myFunction() {
    var str = 12.234556; 
    str = str.toString().split('.');
    var res = str[1].slice(0, 2);
    document.getElementById("demo").innerHTML = str[0]+'.'+res;
}

// output: 12.23

0
投票

这是用字符串做的

export function withoutRange(number) {
  const str = String(number);
  const dotPosition = str.indexOf('.');
  if (dotPosition > 0) {
    const length = str.substring().length;
    const end = length > 3 ? 3 : length;
    return str.substring(0, dotPosition + end);
  }
  return str;
}

0
投票

所有这些答案似乎有点复杂。我只想从你的数字中减去0.5并使用toFixed()。


0
投票

最有效的解决方案(对于2个小数位)是在调用toFixed()之前减去0.005

function toFixed2( num ) { return (num-0.005).toFixed(2) }

负数也将向下舍入(远离零)。 OP没有透露有关负数的任何信息。


0
投票

我知道已经有很少的工作示例,但我认为值得提出我的String.toFixed等价物,有些人可能觉得它很有帮助。

这是我的toFixed替代方案,它不会对数字进行舍入,只是截断它或根据给定的精度添加零。对于超长数字,当精度很长时,它使用JS内置舍入。函数适用于我在堆栈上找到的所有有问题的数字。

function toFixedFixed(value, precision = 0) {
    let stringValue = isNaN(+value) ? '0' : String(+value);

    if (stringValue.indexOf('e') > -1 || stringValue === 'Infinity' || stringValue === '-Infinity') {
        throw new Error('To large number to be processed');
    }

    let [ beforePoint, afterPoint ] = stringValue.indexOf('.') > -1 ? stringValue.split('.') : [ stringValue, ''];

    // Force automatic rounding for some long real numbers that ends with 99X, by converting it to string, cutting off last digit, then adding extra nines and casting it on number again
    // e.g. 2.0199999999999996: +('2.019999999999999' + '9999') will give 2.02
    if (stringValue.length >= 17 && afterPoint.length > 2 && +afterPoint.substr(afterPoint.length - 3) > 995) {
        stringValue = String(+(stringValue.substr(0, afterPoint.length - 1) + '9'.repeat(stringValue.split('.').shift().length + 4)));
        [ beforePoint, afterPoint ] = String(stringValue).indexOf('.') > -1 ? stringValue.split('.') : [ stringValue, ''];
    }

    if (precision === 0) {
        return beforePoint;
    } else if (afterPoint.length > precision) {
        return `${beforePoint}.${afterPoint.substr(0, precision)}`;
    } else {
        return `${beforePoint}.${afterPoint}${'0'.repeat(precision - afterPoint.length)}`;
    }
}

请记住,对于长度为18或更大的数字,可能无法正确处理精度,例如: 64位Chrome会将其四舍五入或添加“e +”/“e-”以使数字长度保持在18。

如果你想对实数进行操作,首先将它乘以Math.sqrt(10, precision)更安全,进行计算,然后通过乘数的值进行潜水。

例:

0.06 + 0.010.06999999999999999,并且每个没有舍入的格式化函数都会将其截断为0.06以获得精确的2

但是如果你用倍增器和分频器执行相同的操作:(0.06 * 100 + 0.01 * 100) / 100,你会得到0.07

这就是为什么在处理javascript中的实数时使用这样的乘数/除法器非常重要,特别是当你计算钱时...


0
投票

我在打字稿中的解决方案(可以很容易地移植到JS):

/**
 * Returns the price with correct precision as a string
 *
 * @param   price The price in decimal to be formatted.
 * @param   decimalPlaces The number of decimal places to use
 * @return  string The price in Decimal formatting.
 */
type toDecimal = (price: number, decimalPlaces?: number) => string;
const toDecimalOdds: toDecimal = (
  price: number,
  decimalPlaces: number = 2,
): string => {
  const priceString: string = price.toString();
  const pointIndex: number = priceString.indexOf('.');

  // Return the integer part if decimalPlaces is 0
  if (decimalPlaces === 0) {
    return priceString.substr(0, pointIndex);
  }

  // Return value with 0s appended after decimal if the price is an integer
  if (pointIndex === -1) {
    const padZeroString: string = '0'.repeat(decimalPlaces);

    return `${priceString}.${padZeroString}`;
  }

  // If numbers after decimal are less than decimalPlaces, append with 0s
  const padZeroLen: number = priceString.length - pointIndex - 1;
  if (padZeroLen > 0 && padZeroLen < decimalPlaces) {
    const padZeroString: string = '0'.repeat(padZeroLen);

    return `${priceString}${padZeroString}`;
  }

  return priceString.substr(0, pointIndex + decimalPlaces + 1);
};

测试用例:

  expect(filters.toDecimalOdds(3.14159)).toBe('3.14');
  expect(filters.toDecimalOdds(3.14159, 2)).toBe('3.14');
  expect(filters.toDecimalOdds(3.14159, 0)).toBe('3');
  expect(filters.toDecimalOdds(3.14159, 10)).toBe('3.1415900000');
  expect(filters.toDecimalOdds(8.2)).toBe('8.20');

有什么改进?


29
投票

我选择写这个来手动删除字符串的余数,所以我不必处理数字带来的数学问题:

num = num.toString(); //If it's not already a String
num = num.slice(0, (num.indexOf("."))+3); //With 3 exposing the hundredths place
Number(num); //If you need it back as a Number

这将给你“15.77”与num = 15.7784514;


0
投票

另一个截断和舍入的解决方案:

function round (number, decimals, truncate) {
    if (truncate) {
        number = number.toFixed(decimals + 1);
        return parseFloat(number.slice(0, -1));
    }

    var n = Math.pow(10.0, decimals);
    return Math.round(number * n) / n;
};

14
投票

October 2017

对于任何n≥0,将数字截断(不舍入)到第n个十进制数并将其转换为精确n个十进制数字的字符串的一般解决方案。

function toFixedTrunc(x, n) {
  const v = (typeof x === 'string' ? x : x.toString()).split('.');
  if (n <= 0) return v[0];
  let f = v[1] || '';
  if (f.length > n) return `${v[0]}.${f.substr(0,n)}`;
  while (f.length < n) f += '0';
  return `${v[0]}.${f}`
}

其中x可以是数字(可以转换为字符串)或字符串。

以下是n = 2的一些测试(包括OP请求的测试):

0           => 0.00
0.01        => 0.01
0.2372      => 0.23
0.5839      => 0.58
0.999       => 0.99
1           => 1.00
1.01        => 1.01
2           => 2.00
2.551       => 2.55
2.5         => 2.50
2.99999     => 2.99
4.27        => 4.27
15.7784514  => 15.77
123.5999    => 123.59

注意:根据ecma specs.toString()为(10 ^ -6,10 ^ 21)范围之外的数字返回exponential形式。因此,任何使用.toString()(包括已接受的)的解决方案都需要采取额外的步骤,如果输入值可以超出该范围。特别是,在将函数传递给函数之前,x可以转换为没有指数表示法的字符串,即toFixedTrunc(noExpNotation(x), n)。有关如何实现noExpNotation的一些信息可以找到here,但这超出了这个答案的范围。


13
投票

parseInt比Math.floor快

function floorFigure(figure, decimals){
    if (!decimals) decimals = 2;
    var d = Math.pow(10,decimals);
    return (parseInt(figure*d)/d).toFixed(decimals);
};

floorFigure(123.5999)    =>   "123.59"
floorFigure(123.5999, 3) =>   "123.599"

6
投票

这很简单

number = parseInt(number * 100)/100;

5
投票

这里的答案对我没有帮助,它继续四舍五入或给我错误的小数。

我的解决方案将您的小数转换为字符串,提取字符然后将整个事物作为数字返回。

function Dec2(num) {
  num = String(num);
  if(num.indexOf('.') !== -1) {
    var numarr = num.split(".");
    if (numarr.length == 1) {
      return Number(num);
    }
    else {
      return Number(numarr[0]+"."+numarr[1].charAt(0)+numarr[1].charAt(1));
    }
  }
  else {
    return Number(num);
  }  
}

Dec2(99); // 99
Dec2(99.9999999); // 99.99
Dec2(99.35154); // 99.35
Dec2(99.8); // 99.8
Dec2(10265.985475); // 10265.98

5
投票
num = 19.66752
f = num.toFixed(3).slice(0,-1)
alert(f)

这将返回19.66


5
投票

我的正数版本:

function toFixed_norounding(n,p)
{
    var result = n.toFixed(p);
    return result <= n ? result: (result - Math.pow(0.1,p)).toFixed(p);
}

快速,漂亮,明显。 (正数版本)

© www.soinside.com 2019 - 2024. All rights reserved.