简体   繁体   English

如何在Javascript中获取浮点数的小数位?

[英]How do I get the decimal places of a floating point number in Javascript?

What I would like to have is the almost opposite of Number.prototype.toPrecision(), meaning that when i have number, how many decimals does it have?我想要的是与 Number.prototype.toPrecision() 几乎相反的,这意味着当我有数字时,它有多少个小数? Eg例如

(12.3456).getDecimals() // 4

For anyone wondering how to do this faster (without converting to string), here's a solution: 对于任何想知道如何更快地做到这一点(没有转换为字符串)的人,这是一个解决方案:

function precision(a) {
  var e = 1;
  while (Math.round(a * e) / e !== a) e *= 10;
  return Math.log(e) / Math.LN10;
}

Edit: a more complete solution with edge cases covered: 编辑:涵盖边缘案例的更完整的解决方案:

function precision(a) {
  if (!isFinite(a)) return 0;
  var e = 1, p = 0;
  while (Math.round(a * e) / e !== a) { e *= 10; p++; }
  return p;
}

一种可能的解决方案(取决于应用程序):

var precision = (12.3456 + "").split(".")[1].length;

If by "precision" you mean "decimal places", then that's impossible because floats are binary . 如果用“precision”表示“小数位”,则那是不可能的,因为浮点数是二进制的 They don't have decimal places, and most values that have a small number of decimal places have recurring digits in binary, and when they're translated back to decimal that doesn't necessarily yield the original decimal number. 他们没有小数位,并有一个小的小数位数的最值具有二进制重复数字,当他们翻译回小数,这并不一定产生原始的十进制数。

Any code that works with the "decimal places" of a float is liable to produce unexpected results on some numbers. 任何与浮点数“小数位”一起使用的代码都可能会对某些数字产生意外结果。

Basing on @blackpla9ue comment and considering numbers exponential format: 基于@ blackpla9ue注释并考虑数字指数格式:

function getPrecision (num) {
  var numAsStr = num.toFixed(10); //number can be presented in exponential format, avoid it
  numAsStr = numAsStr.replace(/0+$/g, '');

  var precision = String(numAsStr).replace('.', '').length - num.toFixed().length;
  return precision;  
}

getPrecision(12.3456);         //4
getPrecision(120.30003300000); //6, trailing zeros are truncated
getPrecision(15);              //0
getPrecision(120.000))         //0
getPrecision(0.0000005);       //7
getPrecision(-0.01))           //2

Based on @boolean_Type's method of handling exponents, but avoiding the regex: 基于@ boolean_Type处理指数的方法,但避免使用正则表达式:

function getPrecision (value) {
    if (!isFinite(value)) { return 0; }

    const [int, float = ''] = Number(value).toFixed(12).split('.');

    let precision = float.length;
    while (float[precision - 1] === '0' && precision >= 0) precision--;

    return precision;
}

Try the following 请尝试以下方法

function countDecimalPlaces(number) { 
  var str = "" + number;
  var index = str.indexOf('.');
  if (index >= 0) {
    return str.length - index - 1;
  } else {
    return 0;
  }
}

There is no native function to determine the number of decimals. 没有本机函数来确定小数位数。 What you can do is convert the number to string and then count the offset off the decimal delimiter . 你可以做的是将数字转换为字符串,然后计算小数分隔符的偏移量. :

Number.prototype.getPrecision = function() {
    var s = this + "",
        d = s.indexOf('.') + 1;

    return !d ? 0 : s.length - d;
};

(123).getPrecision() === 0;
(123.0).getPrecision() === 0;
(123.12345).getPrecision() === 5;
(1e3).getPrecision() === 0;
(1e-3).getPrecision() === 3;

But it's in the nature of floats to fool you. 但是,浮游物的本质就是愚弄你。 1 may just as well be represented by 0.00000000989 or something. 1也可以用0.00000000989或其他东西来表示。 I'm not sure how well the above actually performs in real life applications. 我不确定上述实际应用程序在实际应用程序中的表现如何。

Does the following approaches work? 以下方法是否有效?

var num = 12.3456
console.log(num - Math.floor(num))

or 要么

console.log(num - parseInt(num))

Here are a couple of examples, one that uses a library ( BigNumber.js ), and another that doesn't use a library. 以下是一些示例,一个使用库( BigNumber.js ),另一个不使用库。 Assume you want to check that a given input number ( inputNumber ) has an amount of decimal places that is less than or equal to a maximum amount of decimal places ( tokenDecimals ). 假设您要检查给定的输入数字( inputNumber )是否具有小于或等于最大小数位数( tokenDecimals )的小数位数。

With BigNumber.js 使用BigNumber.js

import BigNumber from 'bignumber.js'; // ES6
// const BigNumber = require('bignumber.js').default; // CommonJS

const tokenDecimals = 18;
const inputNumber = 0.000000000000000001;
// Convert to BigNumber
const inputNumberBn = new BigNumber(inputNumber);

// BigNumber.js API Docs: http://mikemcl.github.io/bignumber.js/#dp
console.log(`Invalid?: ${inputNumberBn.dp() > tokenDecimals}`);

Without BigNumber.js 没有BigNumber.js

function getPrecision(numberAsString) {
  var n = numberAsString.toString().split('.');
  return n.length > 1
    ? n[1].length
    : 0;
}

const tokenDecimals = 18;
const inputNumber = 0.000000000000000001;

// Conversion of number to string returns scientific conversion
// So obtain the decimal places from the scientific notation value
const inputNumberDecimalPlaces = inputNumber.toString().split('-')[1];

// Use `toFixed` to convert the number to a string without it being
// in scientific notation and with the correct number decimal places
const inputNumberAsString = inputNumber.toFixed(inputNumberDecimalPlaces);

// Check if inputNumber is invalid due to having more decimal places
// than the permitted decimal places of the token
console.log(`Invalid?: ${getPrecision(inputNumberAsString) > tokenDecimals}`);

Assuming number is valid. 假设数字有效。

let number = 0.999; 
let noOfPlaces = number.includes(".") //includes or contains
                        ? number.toString().split(".").pop().length
                        : 0;  

Here is a simple solution这是一个简单的解决方案

First of all, if you pass a simple float value as 12.1234 then most of the below/above logics may work but if you pass a value as 12.12340, then it may exclude a count of 0. For eg, if the value is 12.12340 then it may give you a result of 4 instead of 5. As per your problem statement, if you ask javascript to split and count your float value into 2 integers then it won't include trailing 0s of it.首先,如果您将一个简单的浮点值作为 12.1234 传递,那么下面/上面的大多数逻辑可能会起作用,但是如果您将一个值作为 12.12340 传递,那么它可能会排除计数 0。例如,如果该值为 12.12340,则它可能会给您 4 而不是 5 的结果。根据您的问题陈述,如果您要求 javascript 将您的浮点值拆分并计算为 2 个整数,那么它将不包括它的尾随 0。

Let's satisfy our requirement here with a trick ;)让我们用一个技巧来满足我们的要求;)

In the below function you need to pass a value in string format and it will do your work在下面的函数中,您需要以字符串格式传递一个值,它会完成您的工作

function getPrecision(value){
a = value.toString()
console.log('a ->',a)
b = a.split('.')
console.log('b->',b)
return b[1].length

getPrecision('12.12340') // Call a function

For an example, run the below logic例如,运行以下逻辑

 value = '12.12340' a = value.toString() b = a.split('.') console.log('count of trailing decimals->',b[1].length)

That's it!就是这样! It will give you the exact count for normal float values as well as the float values with trailing 0s!它将为您提供正常浮点值以及尾随 0 的浮点值的准确计数!

Thank you!谢谢!

This answer adds to Mourner's accepted solution by making the function more robust.这个答案通过使函数更加健壮,增加了 Mourner 接受的解决方案。 As noted by many, floating point precision makes such a function unreliable.正如许多人所指出的,浮点精度使这样的功能不可靠。 For example, precision(0.1+0.2) yields 17 rather than 1 (this might be computer specific, but for this example see https://jsfiddle.net/s0v17jby/5/ ).例如, precision(0.1+0.2)产生 17 而不是 1(这可能是特定于计算机的,但有关此示例,请参阅https://jsfiddle.net/s0v17jby/5/ )。

IMHO, there are two ways around this: 1. either properly define a decimal type, using eg https://github.com/MikeMcl/decimal.js/ , or 2. define an acceptable precision level which is both OK for your use case and not a problem for the js Number representation (8 bytes can safely represent a total of 16 digits AFAICT).恕我直言,有两种解决方法:1. 使用例如https://github.com/MikeMcl/decimal.js/正确定义十进制类型,或 2. 定义一个可接受的精度级别,这两者都适合您的使用大小写对于 js 数字表示不是问题(8 个字节可以安全地表示总共 16 位 AFAICT)。 For the latter workaround, one can write a more robust variant of the proposed function:对于后一种解决方法,可以编写提议函数的更健壮的变体:

const MAX_DECIMAL_PRECISION = 9; /* must be <= 15 */
const maxDecimalPrecisionFloat = 10**MAX_DECIMAL_PRECISION;

function precisionRobust(a) {
  if (!isFinite(a)) return 0;
  var e = 1, p = 0;
  while ( ++p<=MAX_DECIMAL_PRECISION && Math.round( ( Math.round(a * e) / e - a ) * maxDecimalPrecisionFloat ) !== 0) e *= 10;
  return p-1;
}

In the above example, the maximum precision of 9 means this accepts up to 6 digits before the decimal point and 9 after (so this would work for numbers less than one million and with a maximum of 9 decimal points).在上面的示例中,最大精度为 9 意味着它最多接受小数点前 6 位和小数点后 9 位(因此这适用于小于一百万且最多包含 9 个小数点的数字)。 If your use-case numbers are smaller then you can choose to make this precision even greater (but with a maximum of 15).如果您的用例数量较小,那么您可以选择使此精度更高(但最多为 15)。 It turns out that, for calculating precision, this function seems to do OK on larger numbers as well (though that would no longer be the case if we were, say, adding two rounded numbers within the precisionRobust function).事实证明,对于计算精度,这个函数似乎也适用于较大的数字(尽管如果我们在 precisionRobust 函数中添加两个舍入数字,情况将不再如此)。

Finally, since we now know the maximum useable precision, we can further avoid infinite loops (which I have not been able to replicate but which still seem to cause problems for some).最后,由于我们现在知道了最大可用精度,我们可以进一步避免无限循环(我无法复制它,但它似乎仍然会给某些人带来问题)。

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

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