在 javascript 中四舍五入到任意数量的有效数字不起作用
rounding to an arbitrary number of significant digits in javascript is not working
我尝试了下面的示例代码
function sigFigs(n, sig) {
if ( n === 0 )
return 0
var mult = Math.pow(10,
sig - Math.floor(Math.log(n < 0 ? -n: n) / Math.LN10) - 1);
return Math.round(n * mult) / mult;
}
但是这个函数不适用于像这样的输入
sigFigs(24730790,3) returns 24699999.999999996
和 sigFigs(4.7152e-26,3) returns: 4.7200000000000004e-26
如果有人有工作示例,请分享。
谢谢
您可以尝试javascript内置方法-
Number( my_number.toPrecision(3) )
根据您的情况尝试
Number( 24730790.0.toPrecision(5) )
您可以查看 link
作为参考和工作示例
不幸的是,当数字大于 10 时,内置方法会给你愚蠢的结果,比如指数符号等。
我做了一个函数,应该可以解决这个问题(也许不是最优雅的写法,但就是这样):
function(value, precision) {
if (value < 10) {
value = parseFloat(value).toPrecision(precision)
} else {
value = parseInt(value)
let significantValue = value
for (let i = value.toString().length; i > precision; i--) {
significantValue = Math.round(significantValue / 10)
}
for (let i = 0; significantValue.toString().length < value.toString().length; i++ ) {
significantValue = significantValue * 10
}
value = significantValue
}
return value
}
如果您更喜欢用指数表示法表示较大的数字,请随意使用 toPrecision() 方法。
首先要感谢大家,如果没有分享这些片段,这将是一项艰巨的任务。
我的增值是以下代码片段(完整实施见下文)
parseFloat(number.toPrecision(precision))
请注意,例如,如果数字为 10000,精度为 2,则 number.toPrecision(precision)
将为“1.0e+4”,但 parseFloat
理解指数表示法。
还值得一提的是,信不信由你,上面使用 Math.pow
和对数 post 的算法,当 运行 在测试用例 formatNumber(5, 123456789)
上在 Mac(节点 v12)上成功,但在 Windows(节点 v10)上出现错误。这很奇怪,所以我们得出了上面的解决方案。
最后,我发现这是最终的实施,利用了此 post 中提供的所有反馈。假设我们有一个 formatNumber.js 文件,其内容如下
/**
* Format number to significant digits.
*
* @param {Number} precision
* @param {Number} number
*
* @return {String} formattedValue
*/
export default function formatNumber (precision, number) {
if (typeof number === 'undefined' || number === null) return ''
if (number === 0) return '0'
const roundedValue = round(precision, number)
const floorValue = Math.floor(roundedValue)
const isInteger = Math.abs(floorValue - roundedValue) < Number.EPSILON
const numberOfFloorDigits = String(floorValue).length
const numberOfDigits = String(roundedValue).length
if (numberOfFloorDigits > precision) {
return String(floorValue)
} else {
const padding = isInteger ? precision - numberOfFloorDigits : precision - numberOfDigits + 1
if (padding > 0) {
if (isInteger) {
return `${String(floorValue)}.${'0'.repeat(padding)}`
} else {
return `${String(roundedValue)}${'0'.repeat(padding)}`
}
} else {
return String(roundedValue)
}
}
}
function round (precision, number) {
return parseFloat(number.toPrecision(precision))
}
如果您使用磁带进行测试,这里有一些基本测试
import test from 'tape'
import formatNumber from '..path/to/formatNumber.js'
test('formatNumber', (t) => {
t.equal(formatNumber(4, undefined), '', 'undefined number returns an empty string')
t.equal(formatNumber(4, null), '', 'null number return an empty string')
t.equal(formatNumber(4, 0), '0')
t.equal(formatNumber(4, 1.23456789), '1.235')
t.equal(formatNumber(4, 1.23), '1.230')
t.equal(formatNumber(4, 123456789), '123500000')
t.equal(formatNumber(4, 1234567.890123), '1235000')
t.equal(formatNumber(4, 123.4567890123), '123.5')
t.equal(formatNumber(4, 12), '12.00')
t.equal(formatNumber(4, 1.2), '1.200')
t.equal(formatNumber(4, 1.234567890123), '1.235')
t.equal(formatNumber(4, 0.001234567890), '0.001235')
t.equal(formatNumber(5, 123456789), '123460000')
t.end()
})
自动类型转换如何处理指数表示法?
f = (x, n) => +x.toPrecision(n)
测试:
> f (0.123456789, 6)
0.123457
> f (123456789, 6)
123457000
> f (-123456789, 6)
-123457000
> f (-0.123456789, 6)
-0.123457
> f (-0.123456789, 2)
-0.12
> f (123456789, 2)
120000000
它 returns 是一个数字而不是字符串。
如果要指定小数点左边的有效数字,将多余的占位符分别替换为 T B M K
// example to 3 sigDigs (significant digits)
//54321 = 54.3M
//12300000 = 12.3M
const moneyFormat = (num, sigDigs) => {
var s = num.toString();
let nn = "";
for (let i = 0; i <= s.length; i++) {
if (s[i] !== undefined) {
if (i < sigDigs) nn += s[i];
else nn += "0";
}
}
nn = nn
.toString()
.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, ",")
.replace(",000,000,000", "B")
.replace(",000,000", "M")
.replace(",000", "k");
if (
nn[nn.length - 4] === "," &&
nn[nn.length - 2] === "0" &&
nn[nn.length - 1] === "0"
) {
let numLetter = "K";
if (parseInt(num) > 999999999999) numLetter = "T";
else if (parseInt(num) > 999999999) numLetter = "B";
else if (parseInt(num) > 999999) numLetter = "M";
console.log("numLetter: " + numLetter);
nn = nn.toString();
let nn2 = ""; // new number 2
for (let i = 0; i < nn.length - 4; i++) {
nn2 += nn[i];
}
nn2 += "." + nn[nn.length - 3] + numLetter;
nn = nn2;
}
return nn;
};
我尝试了下面的示例代码
function sigFigs(n, sig) {
if ( n === 0 )
return 0
var mult = Math.pow(10,
sig - Math.floor(Math.log(n < 0 ? -n: n) / Math.LN10) - 1);
return Math.round(n * mult) / mult;
}
但是这个函数不适用于像这样的输入 sigFigs(24730790,3) returns 24699999.999999996 和 sigFigs(4.7152e-26,3) returns: 4.7200000000000004e-26
如果有人有工作示例,请分享。 谢谢
您可以尝试javascript内置方法-
Number( my_number.toPrecision(3) )
根据您的情况尝试
Number( 24730790.0.toPrecision(5) )
您可以查看 link
作为参考和工作示例不幸的是,当数字大于 10 时,内置方法会给你愚蠢的结果,比如指数符号等。
我做了一个函数,应该可以解决这个问题(也许不是最优雅的写法,但就是这样):
function(value, precision) {
if (value < 10) {
value = parseFloat(value).toPrecision(precision)
} else {
value = parseInt(value)
let significantValue = value
for (let i = value.toString().length; i > precision; i--) {
significantValue = Math.round(significantValue / 10)
}
for (let i = 0; significantValue.toString().length < value.toString().length; i++ ) {
significantValue = significantValue * 10
}
value = significantValue
}
return value
}
如果您更喜欢用指数表示法表示较大的数字,请随意使用 toPrecision() 方法。
首先要感谢大家,如果没有分享这些片段,这将是一项艰巨的任务。
我的增值是以下代码片段(完整实施见下文)
parseFloat(number.toPrecision(precision))
请注意,例如,如果数字为 10000,精度为 2,则 number.toPrecision(precision)
将为“1.0e+4”,但 parseFloat
理解指数表示法。
还值得一提的是,信不信由你,上面使用 Math.pow
和对数 post 的算法,当 运行 在测试用例 formatNumber(5, 123456789)
上在 Mac(节点 v12)上成功,但在 Windows(节点 v10)上出现错误。这很奇怪,所以我们得出了上面的解决方案。
最后,我发现这是最终的实施,利用了此 post 中提供的所有反馈。假设我们有一个 formatNumber.js 文件,其内容如下
/**
* Format number to significant digits.
*
* @param {Number} precision
* @param {Number} number
*
* @return {String} formattedValue
*/
export default function formatNumber (precision, number) {
if (typeof number === 'undefined' || number === null) return ''
if (number === 0) return '0'
const roundedValue = round(precision, number)
const floorValue = Math.floor(roundedValue)
const isInteger = Math.abs(floorValue - roundedValue) < Number.EPSILON
const numberOfFloorDigits = String(floorValue).length
const numberOfDigits = String(roundedValue).length
if (numberOfFloorDigits > precision) {
return String(floorValue)
} else {
const padding = isInteger ? precision - numberOfFloorDigits : precision - numberOfDigits + 1
if (padding > 0) {
if (isInteger) {
return `${String(floorValue)}.${'0'.repeat(padding)}`
} else {
return `${String(roundedValue)}${'0'.repeat(padding)}`
}
} else {
return String(roundedValue)
}
}
}
function round (precision, number) {
return parseFloat(number.toPrecision(precision))
}
如果您使用磁带进行测试,这里有一些基本测试
import test from 'tape'
import formatNumber from '..path/to/formatNumber.js'
test('formatNumber', (t) => {
t.equal(formatNumber(4, undefined), '', 'undefined number returns an empty string')
t.equal(formatNumber(4, null), '', 'null number return an empty string')
t.equal(formatNumber(4, 0), '0')
t.equal(formatNumber(4, 1.23456789), '1.235')
t.equal(formatNumber(4, 1.23), '1.230')
t.equal(formatNumber(4, 123456789), '123500000')
t.equal(formatNumber(4, 1234567.890123), '1235000')
t.equal(formatNumber(4, 123.4567890123), '123.5')
t.equal(formatNumber(4, 12), '12.00')
t.equal(formatNumber(4, 1.2), '1.200')
t.equal(formatNumber(4, 1.234567890123), '1.235')
t.equal(formatNumber(4, 0.001234567890), '0.001235')
t.equal(formatNumber(5, 123456789), '123460000')
t.end()
})
自动类型转换如何处理指数表示法?
f = (x, n) => +x.toPrecision(n)
测试:
> f (0.123456789, 6)
0.123457
> f (123456789, 6)
123457000
> f (-123456789, 6)
-123457000
> f (-0.123456789, 6)
-0.123457
> f (-0.123456789, 2)
-0.12
> f (123456789, 2)
120000000
它 returns 是一个数字而不是字符串。
如果要指定小数点左边的有效数字,将多余的占位符分别替换为 T B M K
// example to 3 sigDigs (significant digits)
//54321 = 54.3M
//12300000 = 12.3M
const moneyFormat = (num, sigDigs) => {
var s = num.toString();
let nn = "";
for (let i = 0; i <= s.length; i++) {
if (s[i] !== undefined) {
if (i < sigDigs) nn += s[i];
else nn += "0";
}
}
nn = nn
.toString()
.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, ",")
.replace(",000,000,000", "B")
.replace(",000,000", "M")
.replace(",000", "k");
if (
nn[nn.length - 4] === "," &&
nn[nn.length - 2] === "0" &&
nn[nn.length - 1] === "0"
) {
let numLetter = "K";
if (parseInt(num) > 999999999999) numLetter = "T";
else if (parseInt(num) > 999999999) numLetter = "B";
else if (parseInt(num) > 999999) numLetter = "M";
console.log("numLetter: " + numLetter);
nn = nn.toString();
let nn2 = ""; // new number 2
for (let i = 0; i < nn.length - 4; i++) {
nn2 += nn[i];
}
nn2 += "." + nn[nn.length - 3] + numLetter;
nn = nn2;
}
return nn;
};