确定浮点数是否是另一个浮点数的倍数?

时间:2012-09-14 17:36:54

标签: javascript floating-point

我需要确定浮点数是否是使用JavaScript的另一个浮点数的倍数。

我已经阅读了一些关于浮点数的其他问题,并且我已经了解到它们与模运算符(%)无法正常工作。我还读过你可以通过乘以10/100/1000等将浮点数转换为整数,但这在所有情况下都无法正常工作。

示例:

var A = 25.13;
var B = .0001;

var Value = A*1e5;
var Step  = B*1e5;

// Is Value a multiple of Step?
if(0 === (Value % Step)) {
// Do something
}

在这种情况下,Value是Step的倍数,它可以正常工作。但是怎么样:

var A = 2.2;
var B = .0001;

它应该是一个有效的倍数,但我们得到:

220000.00000000003 % 10 = 2.9103830456733704e-11

在第11个小数位置有一个错误的3。我认为我可以用toFixed()进行舍入来纠正问题:

var Value = (A*1e5).toFixed(10);
var Step  = (B*1e5).toFixed(10);

但如果你这样做:

var A = 45436212356482;
var B = .0001;

你得到:

4543621235648200192.0000000000 % 10.0000000000=2

这是一个有效的倍数,但它认为不是。

使用:

var A = 45436212546522156.45621565421;
var B = .0001;

这不是一个有效的倍数,但它认为它是:

4.543621254652216e+21 % 10.0000000000=0

是否有一个聪明的技巧来确定一个浮点是否是另一个的倍数?或者这不可能吗?

更新

目标是将用户输入的数字(整数或小数)限制为某个增量。

  • 如果增量为1,则用户可以输入1,2,3,4等
  • 如果增量为.5,则用户可以输入.5,1,1.5,2,2.5等。
  • 如果增量为.0002,则用户可以输入1,1.001,1.0004,1.0006,但不能输入1.0001

从逻辑的角度来看,给定的值是或不是给定增量的有效倍数。

6 个答案:

答案 0 :(得分:4)

鉴于您的最后两个示例,使用4543621235648200192等,您似乎希望接受的数字是.0001的整数倍,并拒绝那些不想使用的数字。 0001但使用包含最接近.0001的浮点值的变量。

一般来说,这是不可能的。当你传递其他东西时,算法无法“知道”.0001是有意的。

如果您更多地限制问题,可能会有解决方案。例如,有可能(可能不容易)回答这个问题:浮点值X是否最接近.0001的整数倍的浮点值? (换句话说,是否存在一个整数k,使得.0001乘以k并舍入到最近的浮点值会产生正好的X?)

因此,要获得解决方案,您需要进一步描述您的目标。您是否想要Step的任意值或某些值的解决方案?由于二进制浮点不能足够精确地表示Step,您还有其他方法来描述它吗?例如,它总是是.0001的倍数?您想要接受的值是多数,总是最接近精确数学倍数的二进制浮点数,还是可能有其他错误?

答案 1 :(得分:1)

基本问题是32位浮点数不能正确表示所有实数。例如,如果用户输入0.1,则浮点数的值为0.099999994。

因此,如果您的增量为0.1,则无法判断他是否输入了0.1(这将是有效的),或者他是否输入0.09999 ..(这将是无效的)。

我的建议是使用整数数据类型,并将其视为固定点数。这样你就不会失去精确度,并且可以轻松检查多重性。

答案 2 :(得分:0)

由于您正在处理浮点数,因此最好的办法是确定“近距离”关闭的方式,然后使用:

function IsMultipleOf(a, b) {
   var result = a % b;
   return (result < 1e-3);
}

答案 3 :(得分:0)

如果我错了,请纠正我但是解决方案如:

function IsMultipleOf(a, b) {
   var result = a % b;
   return (result < 1e-3);
}

只回答问题的一半。假设以下(使用python代码):

dt = 2.4
>>> dt = 2.2999999999999998
t = 13 * dt
>>> t = 29.899999999999999
t % dt
>>> 8.8817841970012523e-16

在这种情况下它会正常工作。现在假设:

dt = 1.4
>>> dt = 1.3999999999999999
t = 3 * dt
>>> t = 4.1999999999999993
t % dt
>>> 1.3999999999999995

由于舍入误差的作用是 t 低于 dt 的下一个倍数, 模数的值更接近 dt 而不是0。

解决此问题的一种可能方法是检查两种情况:

modt = t % dt
(abs(modt) <= tolerance) or (abs(dt - modt) <= tolerance)

我希望tolerance = machine epsilon * abs(dt) / 2based on this answer, 但是模运算以某种方式引入了更多错误。 它看起来像:

tolerance = machine epsilon * max(abs(t), abs(dt)) / 2

处理这项工作,但这只是猜测。可替代地,

tolerance = 2 * machine epsilon * abs(dt)

似乎也可以正常工作。

答案 4 :(得分:0)

以下将适用于所有小数。

var result = Math.round( Math.round(number1 * 100000) % Math.round(number2 * 100000) ) / 100000;

答案 5 :(得分:0)

这是您可以做到的一种方法:

function floatingPointAMultipleOfB(a, b) {
  const precision_a = getNumbersAfterDecimal(a)
  const precision_b = getNumbersAfterDecimal(b)

  if (precision_a > precision_b) return false;

  const int_a = Math.round(multBy10(a, precision_b))
  const int_b = Math.round(multBy10(b, precision_b))
  return int_a % int_b === 0
}


function getNumbersAfterDecimal(n) {
  const exponential = n.toString().split('e');
  if (exponential.length === 2) n = n.toFixed(Math.abs(exponential[1]))
  return (n.toString().split('.')[1] || []).length;
}

function multBy10(val, n) {
  if (n === 0) return val
  return multBy10(val, n-1) * 10
}