JavaScript如何判断一个数字是否是另一个数字的倍数?

40
我正在为一家面料店建立一个相当计算密集的购物车,发现自己需要对用户输入的长度 * 每米基准价进行计算,然后检查结果是否是图案长度的倍数。如果不是倍数,我需要找到最接近图案长度的倍数,并将结果更改为该倍数。
我还需要能够在PHP中执行完全相同的计算,但如果有人可以帮助我解决数学问题,我可以自己转换任何需要翻译的内容。
我正在使用jQuery 1.6.2,并已经完成了计算的第一部分,我只需要检查(米*价格)的结果是否符合图案长度。
非常感谢任何帮助。
编辑:所有这些计算都涉及价格和图案长度的2个小数位。 用户输入的长度也可能包含小数点。

4
不要在货币计算中使用浮点数算法,将价格转换为以分为单位。 - Felix Kling
我将会,目前所有的东西都是浮点数,因为它们是从数据库中输出的 :( - totallyNotLizards
@FelixKling 即使通过乘以100将价格转换为分也不好,因为JS数字精度问题。 - Adam Leggett
1
@AdamLeggett 不正确,因为这样会使用简单的四舍五入。 - Dexygen
6个回答

46

在Javascript和PHP中使用%(取模)运算符,它返回a % b的值,即当a除以b时的余数。当ab的倍数时,余数为零。

例如:

//Javascript
var result = userLength * basePrice;     //Get result
if(result % patternLength){              //Check if there is a remainder
  var remainder = result % patternLength; //Get remainder
  if(remainder >= patternLength / 2)      //If the remainder is larger than half of patternLength, then go up to the next mulitple
    result += patternLength - remainder;
  else                                    //Else - subtract the remainder to go down
    result -= remainder;
}
result = Math.round(result * 100) / 100;  //Round to 2 decimal places

1
我想到了模数,但没有考虑余数可以做什么。这对我有用,非常感谢。 - totallyNotLizards
嘿,我不想写重复的代码,你是说你会写一个快速的 bool 来判断 b 是否是 a 的倍数吗??编辑:下面的答案更好地解释了我的情况。 - thednp
1
JS数字精度问题使其在许多情况下都不够好用。 - Adam Leggett

35

你可以使用模运算来找到除法后的余数,如果余数等于零,则它是一个倍数。

//x and y are both integers
var remainder = x % y;
if (remainder == 0){
//x is a multiple of y
} else {
//x is not a multiple of y
}

如果你使用的数字可以保留两位小数,那么模运算仍然有效。如果不能,请先将两个数字乘以100,然后进行上述检查。


8
请记得对数字进行四舍五入,以避免出现浮点数问题,比如https://dev59.com/vm865IYBdhLWcg3wIrBg。 - Teodor

9
这将避免JavaScript精度问题(如果您的因子y小于5个小数位)。
function isMultiple(x, y) {
    return Math.round(x / y) / (1 / y) === x;
}

上述算法对于非常小的因子 <= 1e-5 将会失败。以下是一种解决方案,适用于所有小于或等于 Number.MAX_SAFE_INTEGER 并移除小数点后的值,即多数具有16个数字的值和所有具有15个数字的值,即大多数非科学值。
如果您正在处理科学值,请使用 big.js

function decimalPlaces(x) {
    const str = ''+ +x;
    const dindex = str.indexOf('.');
    const eindex = str.indexOf('e');
    return (
        (dindex < 0 ? 0 : (eindex < 0 ? str.length : eindex) - dindex - 1) +
        (eindex < 0 ? 0 : Math.max(0, -parseInt(str.slice(eindex + 1)))));
}

function isMultiple(x, y) {
    const xplaces = decimalPlaces(x);
    const yplaces = decimalPlaces(y);
    if (xplaces > yplaces) {
        return false;
    }
    const pfactor = Math.pow(10, yplaces);
    return Math.round(pfactor * x) % Math.round(pfactor * y) === 0;
}

[
    [2.03, 0.01],
    [2.03, 0.0123],
    [2.029999999999, 0.01],
    [2.030000000001, 0.01],
    [0.03, 0.01],
    [240, 20],
    [240, 21],
    [1, 1],
    [4, 2],
    [6, 3],
    [6, 4],
    [1.123456, 0.000001]
].forEach(([number, multiple]) => {
    const result = isMultiple(number, multiple);
    console.log(`isMultiple (${number}, ${multiple}) =`, result);
});


5

在Javascript中有余数运算符(类似于大多数C语言风格的编程语言)。

假设x表示长度,y表示价格,z表示xy的乘积。

var remainder = (z % x) / 100;

if (remainder === 0) {
   // z is a multiple of x
}

要获取与您的结果z最接近的x的倍数,您可以使用Math库中的ceil或floor函数向上(或向下)四舍五入结果。

if (r >= x / 2) {
    a = Math.ceil(z/x)*x;
}
else {
    a = Math.floor(z/x)*x;
}

然后保留两位小数

Math.round(a / 100) * 100;

1

我不确定我是否真正理解了这个任务,因为它对我来说似乎非常简单,但请看一下这段PHP代码:

// --- input ---
$pattern = 12.34;
$input = 24.68;
$precision = 2; // number of decimals

// --- calculation ---

// switch to "fixed point":
$base = pow(10, $precision);
$pattern = round($pattern * $base);
$input = round($input * $base);

if ($input % $pattern) {
  // not an exact multiple
  $input = ceil($input / $pattern) * $pattern;
} else {
  // it is an exact multiple
}

// back to normal precision:
$pattern /= $base;
$input /= $base;

这可以很容易地转换为JavaScript。

$input将是最接近模式的下一个倍数。 如果你只需要那个值,而不需要知道它是否实际上是一个倍数,你也可以简单地这样做:

$input = ceil($input * 100 / $pattern) * $pattern / 100;

0
//roundDown to 2 decimal places function
function r2(n) {
   return Math.floor(n*100)/100; 
}

neededLength = 4.55;
price = 4.63;
patternLength = 1.6;

// price despite the length of the pattern
priceSum = r2(neededLength * price);

// calculate how many pattern lengths there must be to fit into needed length
patternLengths = Math.floor((neededLength+patternLength)/patternLength);
// calculate price for pattern lengths
priceByPatternSum = r2((patternLengths * patternLength) * price );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接